From 2dcf5fc95532c42929f5e3fbad483a4e9eba6320 Mon Sep 17 00:00:00 2001 From: David Terry Date: Sun, 1 Aug 2021 23:59:40 +0200 Subject: [PATCH 1/9] package local remappings This modifies dapp remappings so that it now generates a unique set of remappings for each `src` folder in the dependency tree that points into the adjacent `lib` folder. This allows for multiple versions of a given package in the dependency tree, and means `ds-pain` is happily obsolete. --- src/dapp-tests/integration/tests.sh | 9 ++++ src/dapp-tests/shell.nix | 4 +- src/dapp/libexec/dapp/dapp-remappings | 64 ++++++--------------------- 3 files changed, 25 insertions(+), 52 deletions(-) diff --git a/src/dapp-tests/integration/tests.sh b/src/dapp-tests/integration/tests.sh index bbf48b0be..eaf726281 100755 --- a/src/dapp-tests/integration/tests.sh +++ b/src/dapp-tests/integration/tests.sh @@ -53,6 +53,15 @@ dapp_testnet() { dapp_testnet +# tests the behaviour of the package local dapp remappings +dapp_remappings() { + TMPDIR=$(mktemp -d) + git clone https://github.com/dapphub/remappings-test "$TMPDIR" + (cd "$TMPDIR" && dapp update && dapp test) +} + +dapp_remappings + test_hevm_symbolic() { solc --bin-runtime -o . --overwrite factor.sol # should find counterexample diff --git a/src/dapp-tests/shell.nix b/src/dapp-tests/shell.nix index 95097236a..92d45d634 100644 --- a/src/dapp-tests/shell.nix +++ b/src/dapp-tests/shell.nix @@ -8,11 +8,11 @@ let hypothesis pytest # other python packages you want - ]; + ]; python-with-pkgs = python3.withPackages my-python-packages; in mkShell { name = "dapp-tests"; - buildInputs = [ killall cacert bashInteractive curl dapp gnumake hevm procps seth solc go-ethereum python-with-pkgs ]; + buildInputs = [ killall cacert bashInteractive curl dapp git gnumake hevm procps seth solc go-ethereum python-with-pkgs ]; } diff --git a/src/dapp/libexec/dapp/dapp-remappings b/src/dapp/libexec/dapp/dapp-remappings index eb3814af4..07d31e94d 100755 --- a/src/dapp/libexec/dapp/dapp-remappings +++ b/src/dapp/libexec/dapp/dapp-remappings @@ -1,54 +1,23 @@ #!/usr/bin/env node -var PROGRAM_NAME = process.argv[1].replace(/.*\//, "") -var pkg_src = {}, pkg_hash = {} -findRemappings(".") +console.log(buildRemappings(".").join("\n")) -Object.keys(pkg_src).sort((a, b) => a.length - b.length).sort( - (a, b) => { - var depth = name => pkg_src[name].split("/").length - return depth(a) - depth(b) - } -).forEach(name => { - console.log(`${name}/=${pkg_src[name]}/`) -}) - -function findRemappings(prefix) { - ls(`${prefix}/${process.env.DAPP_LIB}`).forEach(name => { - var lib = `${prefix}/${process.env.DAPP_LIB}` - var src = `${lib}/${name}/${process.env.DAPP_SRC}`.replace(/^.\//, "") - - // Shortcut when we're ignoring all the Git hash stuff. - if (process.env.DAPP_IGNORE_HASHES) { - pkg_src[name] = src - findRemappings(`${lib}/${name}`) - return - } +function buildRemappings(prefix) { + const lib = `${prefix}/${process.env.DAPP_LIB}` + const ctx = `${prefix}/${process.env.DAPP_SRC}` - if (ls(`${lib}/${name}`).includes(".git") != true) { - console.error(`${PROGRAM_NAME}: error: ${lib}/${name} is not a Git repository`) - console.error(`${PROGRAM_NAME}: error: try "dapp update" to initialize submodules`) - process.exit(1) - } - - var hash = run("git", ["-C", src, "rev-parse", "HEAD"]) + const remappings = ls(lib).map(name => { + return `${normalize(ctx)}:${name}/=${normalize(lib)}/${name}/${process.env.DAPP_SRC}/` + }) - if (pkg_src[name]) { - if (hash != pkg_hash[name]) { - console.error(`${PROGRAM_NAME}: warning: mismatching packages:`) - console.error(`${PROGRAM_NAME}: warning: (1) ${pkg_src[name]}`) - console.error(`${PROGRAM_NAME}: warning: (2) ${src}`) - console.error(`${PROGRAM_NAME}: warning: using ${pkg_src[name]}. You can override this using \`DAPP_REMAPPINGS\``) - } else if (src.length < pkg_src[name].length) { - pkg_src[name] = src - } - } else { - pkg_src[name] = src - pkg_hash[name] = hash - } + return ls(lib).map(name => { + return buildRemappings(`${lib}/${name}`) + }).concat(remappings).flat() +} - findRemappings(`${lib}/${name}`) - }) +// strip the leading `.` or `./` from a path +function normalize(path) { + return path.replace(/^\.\//, "").replace(/^\//, "") } function ls(dir) { @@ -59,8 +28,3 @@ function ls(dir) { } } -function run(cmd, args) { - return require("child_process").execFileSync(cmd, args, { - encoding: "utf-8" - }) -} From c2613b1f6d757800691fc95e1e3812aaee556fe4 Mon Sep 17 00:00:00 2001 From: David Terry Date: Wed, 4 Aug 2021 00:31:17 +0200 Subject: [PATCH 2/9] dapp: remappings: deduplicate identical packages --- src/dapp-tests/integration/tests.sh | 3 +- src/dapp/libexec/dapp/dapp-remappings | 62 +++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/dapp-tests/integration/tests.sh b/src/dapp-tests/integration/tests.sh index eaf726281..23c43a9c6 100755 --- a/src/dapp-tests/integration/tests.sh +++ b/src/dapp-tests/integration/tests.sh @@ -55,9 +55,10 @@ dapp_testnet # tests the behaviour of the package local dapp remappings dapp_remappings() { + REV="fde82bd3319f7a1407a21553d120927d99a95f26" TMPDIR=$(mktemp -d) git clone https://github.com/dapphub/remappings-test "$TMPDIR" - (cd "$TMPDIR" && dapp update && dapp test) + (cd "$TMPDIR" && git checkout "$REV" && dapp update && dapp test) } dapp_remappings diff --git a/src/dapp/libexec/dapp/dapp-remappings b/src/dapp/libexec/dapp/dapp-remappings index 07d31e94d..2f0a688e4 100755 --- a/src/dapp/libexec/dapp/dapp-remappings +++ b/src/dapp/libexec/dapp/dapp-remappings @@ -1,18 +1,59 @@ #!/usr/bin/env node +const PROGRAM_NAME = process.argv[1].replace(/.*\//, "") -console.log(buildRemappings(".").join("\n")) +const tree = buildDependencyTree(".") +console.log(buildRemappings(deduplicate(mapHashes(tree), tree)).join("\n")) + +// builds a in memory representation of the projects dependency tree +// +// A node in the tree looks like this: +// +// { +// name: "", +// path: "", +// hash: "", +// deps: [] +// } +function buildDependencyTree(prefix) { + if (ls(prefix).includes(".git") != true) { + console.error(`${PROGRAM_NAME}: error: ${prefix} is not a Git repository`) + console.error(`${PROGRAM_NAME}: error: try "dapp update" to initialize submodules`) + process.exit(1) + } -function buildRemappings(prefix) { const lib = `${prefix}/${process.env.DAPP_LIB}` - const ctx = `${prefix}/${process.env.DAPP_SRC}` + return { + name: prefix.split("/").pop(), + path: normalize(`${prefix}/${process.env.DAPP_SRC}`), + hash: run("git", ["-C", prefix, "rev-parse", "HEAD"]), + deps: ls(lib).map(p => buildDependencyTree(`${lib}/${p}`)) + } +} - const remappings = ls(lib).map(name => { - return `${normalize(ctx)}:${name}/=${normalize(lib)}/${name}/${process.env.DAPP_SRC}/` +// walk tree and build remappings +function buildRemappings(pkg) { + const remappings = pkg.deps.map(dep => { + return `${pkg.path}/:${dep.name}/=${dep.path}/` }) + return pkg.deps.map(buildRemappings).concat(remappings).flat() +} - return ls(lib).map(name => { - return buildRemappings(`${lib}/${name}`) - }).concat(remappings).flat() +// walk tree and build a mapping from hash => path +function mapHashes(pkg) { + const go = (mapping, dep) => { + mapping[dep.hash] = dep.path + return dep.deps.reduce(go, mapping) + } + return tree.deps.reduce(go, { [pkg.hash]: pkg.path }) +} + +// walk tree and rewrite paths so that all packages with the same hash have the same path +function deduplicate(mapping, pkg) { + return { + ...pkg, + path: mapping[pkg.hash], + deps: pkg.deps.map(dep => deduplicate(mapping, dep)) + } } // strip the leading `.` or `./` from a path @@ -28,3 +69,8 @@ function ls(dir) { } } +function run(cmd, args) { + return require("child_process").execFileSync(cmd, args, { + encoding: "utf-8" + }) +} From 1cce6cd9c2f8b4245851d9c2b3cc9eb821e9d9ff Mon Sep 17 00:00:00 2001 From: David Terry Date: Wed, 4 Aug 2021 12:13:57 +0200 Subject: [PATCH 3/9] dapp: remappings: minor refactoring --- src/dapp/libexec/dapp/dapp-remappings | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/dapp/libexec/dapp/dapp-remappings b/src/dapp/libexec/dapp/dapp-remappings index 2f0a688e4..13b23f534 100755 --- a/src/dapp/libexec/dapp/dapp-remappings +++ b/src/dapp/libexec/dapp/dapp-remappings @@ -1,8 +1,7 @@ #!/usr/bin/env node const PROGRAM_NAME = process.argv[1].replace(/.*\//, "") -const tree = buildDependencyTree(".") -console.log(buildRemappings(deduplicate(mapHashes(tree), tree)).join("\n")) +console.log(buildRemappings(deduplicate(buildDependencyTree("."))).join("\n")) // builds a in memory representation of the projects dependency tree // @@ -38,22 +37,20 @@ function buildRemappings(pkg) { return pkg.deps.map(buildRemappings).concat(remappings).flat() } +// walk tree and rewrite paths so that all packages with the same hash have the same path +function deduplicate(pkg) { + const mapping = mapHashes(pkg) + const go = p => ({ ...p, path: mapping[p.hash], deps: p.deps.map(go) }) + return go(pkg) +} + // walk tree and build a mapping from hash => path function mapHashes(pkg) { const go = (mapping, dep) => { mapping[dep.hash] = dep.path return dep.deps.reduce(go, mapping) } - return tree.deps.reduce(go, { [pkg.hash]: pkg.path }) -} - -// walk tree and rewrite paths so that all packages with the same hash have the same path -function deduplicate(mapping, pkg) { - return { - ...pkg, - path: mapping[pkg.hash], - deps: pkg.deps.map(dep => deduplicate(mapping, dep)) - } + return pkg.deps.reduce(go, { [pkg.hash]: pkg.path }) } // strip the leading `.` or `./` from a path From b34a10503ac76ec5bd78d3ab3ca957ddcd2d12a8 Mon Sep 17 00:00:00 2001 From: David Terry Date: Wed, 4 Aug 2021 12:42:51 +0200 Subject: [PATCH 4/9] dapp: remappings: allow non git folders in lib dir --- src/dapp/libexec/dapp/dapp-remappings | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/dapp/libexec/dapp/dapp-remappings b/src/dapp/libexec/dapp/dapp-remappings index 13b23f534..38d0df322 100755 --- a/src/dapp/libexec/dapp/dapp-remappings +++ b/src/dapp/libexec/dapp/dapp-remappings @@ -14,17 +14,11 @@ console.log(buildRemappings(deduplicate(buildDependencyTree("."))).join("\n")) // deps: [] // } function buildDependencyTree(prefix) { - if (ls(prefix).includes(".git") != true) { - console.error(`${PROGRAM_NAME}: error: ${prefix} is not a Git repository`) - console.error(`${PROGRAM_NAME}: error: try "dapp update" to initialize submodules`) - process.exit(1) - } - const lib = `${prefix}/${process.env.DAPP_LIB}` return { name: prefix.split("/").pop(), path: normalize(`${prefix}/${process.env.DAPP_SRC}`), - hash: run("git", ["-C", prefix, "rev-parse", "HEAD"]), + hash: hash(prefix), deps: ls(lib).map(p => buildDependencyTree(`${lib}/${p}`)) } } @@ -58,6 +52,16 @@ function normalize(path) { return path.replace(/^\.\//, "").replace(/^\//, "") } +// computes the hash of the contents of a given directory, uses the git hash if +// availalbe (because it's faster), or falls back to a sha256sum of the directory contents if needed +function hash(dir) { + if (ls(dir).includes(".git")) { + return run("git", ["-C", dir, "rev-parse", "HEAD"]) + } else { + return run("bash", ["-c", `rg --files ${dir} | sort | xargs sha256sum | sha256sum`]) + } +} + function ls(dir) { try { return require("fs").readdirSync(dir).sort() From 90ca996d276398edb588b3aa1042a019562e5130 Mon Sep 17 00:00:00 2001 From: David Terry Date: Wed, 4 Aug 2021 17:53:36 +0200 Subject: [PATCH 5/9] dapp: remappings: add flag for legacy compat --- src/dapp-tests/integration/tests.sh | 11 ++++++++ src/dapp/default.nix | 7 ++--- src/dapp/libexec/dapp/dapp-remappings | 37 ++++++++++++++++++++++----- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/dapp-tests/integration/tests.sh b/src/dapp-tests/integration/tests.sh index 23c43a9c6..7433bb1ae 100755 --- a/src/dapp-tests/integration/tests.sh +++ b/src/dapp-tests/integration/tests.sh @@ -63,6 +63,17 @@ dapp_remappings() { dapp_remappings +# tests dapp remappings on a large legacy project with many implicit imports +dapp_remappings_compat() { + REV="bc6d7657f0f5190f65051543199e1b47bf29932b" + TMPDIR=$(mktemp -d) + git clone https://github.com/dapp-org/tinlake-tests "$TMPDIR" + export DAPP_ALLOW_IMPLICIT_IMPORTS=yes + (cd "$TMPDIR" && git checkout "$REV" && dapp update && dapp --use solc:0.7.6 build) +} + +dapp_remappings_compat + test_hevm_symbolic() { solc --bin-runtime -o . --overwrite factor.sol # should find counterexample diff --git a/src/dapp/default.nix b/src/dapp/default.nix index 427e072b5..4a5ae117c 100644 --- a/src/dapp/default.nix +++ b/src/dapp/default.nix @@ -1,6 +1,7 @@ { lib, stdenv, fetchFromGitHub, makeWrapper, glibcLocales -, coreutils, git, gnused, gnumake, hevm, jshon, jq, nix -, nodejs, perl, python3, seth, shellcheck, solc, tre, dapptoolsSrc }: +, coreutils, findutils, ripgrep, git, gnused, gnumake, hevm +, jshon, jq, nix, nodejs, perl, python3, seth, shellcheck, solc +, tre, dapptoolsSrc }: stdenv.mkDerivation rec { name = "dapp-${version}"; @@ -16,7 +17,7 @@ stdenv.mkDerivation rec { postInstall = let path = lib.makeBinPath [ - coreutils git gnused gnumake hevm jshon jq nix nodejs perl seth solc tre python3 + coreutils findutils git gnused gnumake hevm jshon jq nix nodejs perl seth solc tre python3 ripgrep ]; in '' diff --git a/src/dapp/libexec/dapp/dapp-remappings b/src/dapp/libexec/dapp/dapp-remappings index 38d0df322..30acd026c 100755 --- a/src/dapp/libexec/dapp/dapp-remappings +++ b/src/dapp/libexec/dapp/dapp-remappings @@ -25,10 +25,20 @@ function buildDependencyTree(prefix) { // walk tree and build remappings function buildRemappings(pkg) { - const remappings = pkg.deps.map(dep => { - return `${pkg.path}/:${dep.name}/=${dep.path}/` - }) - return pkg.deps.map(buildRemappings).concat(remappings).flat() + const paths = mapHashes(pkg) + + let implicits = [] + if (process.env.DAPP_ALLOW_IMPLICIT_IMPORTS == "yes") { + const directs = pkg.deps.map(p => p.name) + // get the transitive deps with exactly one version that are not direct deps + const uniques = Object.entries(pkg.deps.reduce(buildVersionMap, {})) + .filter(([name, vs]) => vs.size == 1 && !directs.includes(name)) + // build remappings that allow importing these deps from `pkg` + implicits = uniques.map(([name, v]) => `${pkg.path}/:${name}/=${paths[([...v][0])]}/`) + } + + const remappings = pkg.deps.map(dep => `${pkg.path}/:${dep.name}/=${dep.path}/`) + return pkg.deps.map(buildRemappings).concat(remappings).concat(implicits).flat() } // walk tree and rewrite paths so that all packages with the same hash have the same path @@ -47,6 +57,21 @@ function mapHashes(pkg) { return pkg.deps.reduce(go, { [pkg.hash]: pkg.path }) } +// folds over a dependency tree with map as the accumlator and builds a mapping +// from package name to a set of discovered package verions +function buildVersionMap(map, pkg) { + const update = (map, dep) => { + map[dep.name] == undefined + ? map[dep.name] = new Set([dep.hash]) + : map[dep.name].add(dep.hash) + return map + } + + return pkg.deps.reduce((versions, dep) => { + return update(versions, dep) + }, update(map, pkg)) +} + // strip the leading `.` or `./` from a path function normalize(path) { return path.replace(/^\.\//, "").replace(/^\//, "") @@ -56,9 +81,9 @@ function normalize(path) { // availalbe (because it's faster), or falls back to a sha256sum of the directory contents if needed function hash(dir) { if (ls(dir).includes(".git")) { - return run("git", ["-C", dir, "rev-parse", "HEAD"]) + return run("git", ["-C", dir, "rev-parse", "HEAD"]).trim() } else { - return run("bash", ["-c", `rg --files ${dir} | sort | xargs sha256sum | sha256sum`]) + return run("bash", ["-c", `rg --files ${dir} | sort | xargs sha256sum | sha256sum | cut -d' ' -f1`]).trim() } } From aa5302d24408e5cded3184df234e9b30fc20b16c Mon Sep 17 00:00:00 2001 From: David Terry Date: Thu, 5 Aug 2021 13:44:09 +0200 Subject: [PATCH 6/9] dapp: remappings: document new format, rename & expose legacy compat flag --- src/dapp-tests/integration/tests.sh | 5 +- src/dapp/CHANGELOG.md | 10 +- src/dapp/README.md | 249 +++++++++++++++++++++----- src/dapp/libexec/dapp/dapp | 188 +++++++++---------- src/dapp/libexec/dapp/dapp-remappings | 9 +- 5 files changed, 311 insertions(+), 150 deletions(-) diff --git a/src/dapp-tests/integration/tests.sh b/src/dapp-tests/integration/tests.sh index 7433bb1ae..83b023c79 100755 --- a/src/dapp-tests/integration/tests.sh +++ b/src/dapp-tests/integration/tests.sh @@ -63,13 +63,12 @@ dapp_remappings() { dapp_remappings -# tests dapp remappings on a large legacy project with many implicit imports +# tests dapp remappings on a large legacy project with many transitive imports dapp_remappings_compat() { REV="bc6d7657f0f5190f65051543199e1b47bf29932b" TMPDIR=$(mktemp -d) git clone https://github.com/dapp-org/tinlake-tests "$TMPDIR" - export DAPP_ALLOW_IMPLICIT_IMPORTS=yes - (cd "$TMPDIR" && git checkout "$REV" && dapp update && dapp --use solc:0.7.6 build) + (cd "$TMPDIR" && git checkout "$REV" && dapp update && dapp --use solc:0.7.6 build --allow-transitive-imports) } dapp_remappings_compat diff --git a/src/dapp/CHANGELOG.md b/src/dapp/CHANGELOG.md index 59df61a6c..baa816302 100644 --- a/src/dapp/CHANGELOG.md +++ b/src/dapp/CHANGELOG.md @@ -11,12 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `DAPP_TEST_DEPTH` env var to control `--depth` - `--coverage` flag for `dapp test` to generate coverage via hevm -- `dapp debug` respects the `DAPP_LINK_TEST_LIBRARIES` environment variable. - `dapp install` accepts URLs with git tags, branches or revs specified as `@` ### Changed -- Dapp debug respects DAPP_LINK_TEST_LIBRARIES +- `dapp debug` respects the `DAPP_LINK_TEST_LIBRARIES` environment variable. ### Fixed @@ -28,9 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `hevm` bumped to london hard fork. - `dapp --use` now uses the solc binaries from https://binaries.soliditylang.org/ instead of the - versions built from source via nix -- `dapp remappings` now issues a warning instead of failing with a hard error in case of mistmatched - package versions in the dependency tree + versions built from source via nix +- `dapp remappings` now generates a unique set of remappings for each package in the dependency + tree that point into that pacakge's lib dir, allowing for multiple versions of the same package + to coexist in the dependency tree. More information in the [README](./README.md#package-structure-and-dependency-management). ## [0.33.0] - 2021-07-01 diff --git a/src/dapp/README.md b/src/dapp/README.md index ac3cdbd70..396683fb8 100644 --- a/src/dapp/README.md +++ b/src/dapp/README.md @@ -9,8 +9,6 @@ that isn't available in `rpc`, such as [fuzz testing](#property-based-testing), -**Table of Contents** - - [Installing](#installing) - [Basic usage: a tutorial](#basic-usage-a-tutorial) - [Building](#building) @@ -21,7 +19,12 @@ that isn't available in `rpc`, such as [fuzz testing](#property-based-testing), - [Testing against RPC state](#testing-against-rpc-state) - [Deployment](#deployment) - [Configuration](#configuration) + - [Precedence](#precedence) - [solc version](#solc-version) +- [Package Structure and Dependency Management](#package-structure-and-dependency-management) + - [Import Syntax](#import-syntax) + - [Deduplication](#deduplication) + - [Importing from Transitive Dependencies](#importing-from-transitive-dependencies) - [Commands](#commands) - [`dapp init`](#dapp-init) - [`dapp build`](#dapp-build) @@ -34,6 +37,7 @@ that isn't available in `rpc`, such as [fuzz testing](#property-based-testing), - [`dapp upgrade`](#dapp-upgrade) - [`dapp testnet`](#dapp-testnet) - [`dapp verify-contract`](#dapp-verify-contract) + - [`dapp remappings`](#dapp-remappings) - [`dapp mk-standard-json`](#dapp-mk-standard-json) @@ -281,47 +285,48 @@ Below is a list of the environment variables recognized by `dapp`. You can addit various block parameters when running unit tests by using the [hevm specific environment variables](../hevm/README.md#environment-variables). -| Variable | Default | Synopsis | -| -------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -| `DAPP_SRC` | `src` | Directory for the project's Solidity contracts | -| `DAPP_LIB` | `lib` | Directory for installed Dapp packages | -| `DAPP_OUT` | `out` | Directory for compilation artifacts | -| `DAPP_ROOT` | `.` | Root directory of compilation | -| `DAPP_SOLC_VERSION` | `0.8.6` | Solidity compiler version to use | -| `DAPP_SOLC` | n/a | solc binary to use | -| `DAPP_LIBRARIES` | automatically deployed | Library addresses to link to | -| `DAPP_SKIP_BUILD` | n/a | Avoid compiling this time | -| `DAPP_COVERAGE` | n/a | Print coverage data | -| `DAPP_LINK_TEST_LIBRARIES` | `1` when testing; else `0` | Compile with libraries | -| `DAPP_VERIFY_CONTRACT` | `yes` | Attempt Etherscan verification | -| `DAPP_ASYNC` | n/a | Set to `yes` to skip waiting for etherscan verification to succeed | -| `DAPP_STANDARD_JSON` | `$(dapp mk-standard-json)` | [Solidity compilation options](https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description) | -| `DAPP_REMAPPINGS` | `$(dapp remappings)` | [Solidity remappings](https://docs.soliditylang.org/en/latest/using-the-compiler.html#path-remapping) | -| `DAPP_BUILD_OPTIMIZE` | `0` | Activate Solidity optimizer (`0` or `1`) | -| `DAPP_BUILD_OPTIMIZE_RUNS` | `200` | Set the optimizer runs | -| `DAPP_TEST_MATCH` | n/a | Only run test methods matching a regex | -| `DAPP_TEST_VERBOSITY` | `0` | Sets how much detail `dapp test` logs. Verbosity `1` shows traces for failing tests, `2` shows logs for all tests, `3` shows traces for all tests | -| `DAPP_TEST_FFI ` | `0` | Allow use of the ffi cheatcode in tests (`0` or `1`) | -| `DAPP_TEST_FUZZ_RUNS` | `200` | How many iterations to use for each property test in your project | -| `DAPP_TEST_DEPTH` | `20` | Number of transactions to sequence per invariant cycle | -| `DAPP_TEST_SMTTIMEOUT` | `60000` | Timeout passed to the smt solver for symbolic tests (in ms, and per smt query) | -| `DAPP_TEST_MAX_ITERATIONS` | n/a | The number of times hevm will revisit a particular branching point when symbolically executing | -| `DAPP_TEST_SOLVER` | `z3` | Solver to use for symbolic execution (`cvc4` or `z3`) | -| `DAPP_TEST_MATCH` | n/a | Regex used to determine test methods to run | -| `DAPP_TEST_COV_MATCH` | n/a | Regex used to determine which files to print coverage reports for. Prints all imported files by default (excluding tests and libs). | -| `DAPP_TEST_REPLAY` | n/a | Calldata for a specific property test case to replay in the debugger | -| `HEVM_RPC` | n/a | Set to `yes` to have `hevm` fetch state from rpc when running unit tests | -| `ETH_RPC_URL` | n/a | The url of the rpc server that should be used for any rpc calls | -| `DAPP_TESTNET_RPC_PORT` | `8545` | Which port to expose the rpc server on when running `dapp testnet` | -| `DAPP_TESTNET_RPC_ADDRESS` | `127.0.0.1` | Which ip address to bind the rpc server to when running `dapp testnet` | -| `DAPP_TESTNET_CHAINID` | `99` | Which chain id to use when running `dapp testnet` | -| `DAPP_TESTNET_PERIOD` | `0` | Blocktime to use for `dapp testnet`. `0` means blocks are produced instantly as soon as a transaction is received | -| `DAPP_TESTNET_ACCOUNTS` | `0` | How many extra accounts to create when running `dapp testnet` (At least one is always created) | -| `DAPP_TESTNET_gethdir` | `$HOME/.dapp/testnet` | Root directory that should be used for `dapp testnet` data | -| `DAPP_TESTNET_SAVE` | n/a | Name of the subdirectory under `${DAPP_TESTNET_gethdir}/snapshots` where the chain data from the current `dapp testnet` invocation should be saved | -| `DAPP_TESTNET_LOAD` | n/a | Name of the subdirectory under `${DAPP_TESTNET_gethdir}/snapshots` from which `dapp testnet` chain data should be loaded | -| `DAPP_BUILD_EXTRACT` | n/a | Set to a non null value to output `.abi`, `.bin` and `.bin-runtime` when using `dapp build`. Uses legacy build mode | -| `DAPP_BUILD_LEGACY` | n/a | Set to a non null value to compile using the `--combined-json` flag. This is provided for compatibility with older workflows | +| Variable | Default | Synopsis | +| ------------------------------ | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `DAPP_SRC` | `src` | Directory for the project's Solidity contracts | +| `DAPP_LIB` | `lib` | Directory for installed Dapp packages | +| `DAPP_OUT` | `out` | Directory for compilation artifacts | +| `DAPP_ROOT` | `.` | Root directory of compilation | +| `DAPP_SOLC_VERSION` | `0.8.6` | Solidity compiler version to use | +| `DAPP_SOLC` | n/a | solc binary to use | +| `DAPP_LIBRARIES` | automatically deployed | Library addresses to link to | +| `DAPP_SKIP_BUILD` | n/a | Avoid compiling this time | +| `DAPP_COVERAGE` | n/a | Print coverage data | +| `DAPP_LINK_TEST_LIBRARIES` | `1` when testing; else `0` | Compile with libraries | +| `DAPP_VERIFY_CONTRACT` | `yes` | Attempt Etherscan verification | +| `DAPP_ASYNC` | n/a | Set to `yes` to skip waiting for etherscan verification to succeed | +| `DAPP_ALLOW_TRANSITIVE_IMPORTS`| n/a | Set to `1` to allow imports using the short syntax from transitive dependencies | +| `DAPP_STANDARD_JSON` | `$(dapp mk-standard-json)` | [Solidity compilation options](https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description) | +| `DAPP_REMAPPINGS` | `$(dapp remappings)` | [Solidity remappings](https://docs.soliditylang.org/en/latest/using-the-compiler.html#path-remapping) | +| `DAPP_BUILD_OPTIMIZE` | `0` | Activate Solidity optimizer (`0` or `1`) | +| `DAPP_BUILD_OPTIMIZE_RUNS` | `200` | Set the optimizer runs | +| `DAPP_TEST_MATCH` | n/a | Only run test methods matching a regex | +| `DAPP_TEST_VERBOSITY` | `0` | Sets how much detail `dapp test` logs. Verbosity `1` shows traces for failing tests, `2` shows logs for all tests, `3` shows traces for all tests | +| `DAPP_TEST_FFI ` | `0` | Allow use of the ffi cheatcode in tests (`0` or `1`) | +| `DAPP_TEST_FUZZ_RUNS` | `200` | How many iterations to use for each property test in your project | +| `DAPP_TEST_DEPTH` | `20` | Number of transactions to sequence per invariant cycle | +| `DAPP_TEST_SMTTIMEOUT` | `60000` | Timeout passed to the smt solver for symbolic tests (in ms, and per smt query) | +| `DAPP_TEST_MAX_ITERATIONS` | n/a | The number of times hevm will revisit a particular branching point when symbolically executing | +| `DAPP_TEST_SOLVER` | `z3` | Solver to use for symbolic execution (`cvc4` or `z3`) | +| `DAPP_TEST_MATCH` | n/a | Regex used to determine test methods to run | +| `DAPP_TEST_COV_MATCH` | n/a | Regex used to determine which files to print coverage reports for. Prints all imported files by default (excluding tests and libs). | +| `DAPP_TEST_REPLAY` | n/a | Calldata for a specific property test case to replay in the debugger | +| `HEVM_RPC` | n/a | Set to `yes` to have `hevm` fetch state from rpc when running unit tests | +| `ETH_RPC_URL` | n/a | The url of the rpc server that should be used for any rpc calls | +| `DAPP_TESTNET_RPC_PORT` | `8545` | Which port to expose the rpc server on when running `dapp testnet` | +| `DAPP_TESTNET_RPC_ADDRESS` | `127.0.0.1` | Which ip address to bind the rpc server to when running `dapp testnet` | +| `DAPP_TESTNET_CHAINID` | `99` | Which chain id to use when running `dapp testnet` | +| `DAPP_TESTNET_PERIOD` | `0` | Blocktime to use for `dapp testnet`. `0` means blocks are produced instantly as soon as a transaction is received | +| `DAPP_TESTNET_ACCOUNTS` | `0` | How many extra accounts to create when running `dapp testnet` (At least one is always created) | +| `DAPP_TESTNET_gethdir` | `$HOME/.dapp/testnet` | Root directory that should be used for `dapp testnet` data | +| `DAPP_TESTNET_SAVE` | n/a | Name of the subdirectory under `${DAPP_TESTNET_gethdir}/snapshots` where the chain data from the current `dapp testnet` invocation should be saved | +| `DAPP_TESTNET_LOAD` | n/a | Name of the subdirectory under `${DAPP_TESTNET_gethdir}/snapshots` from which `dapp testnet` chain data should be loaded | +| `DAPP_BUILD_EXTRACT` | n/a | Set to a non null value to output `.abi`, `.bin` and `.bin-runtime` when using `dapp build`. Uses legacy build mode | +| `DAPP_BUILD_LEGACY` | n/a | Set to a non null value to compile using the `--combined-json` flag. This is provided for compatibility with older workflows | A global (always loaded) config file is located in `~/.dapprc`. A local `.dapprc` can also be defined in your project's root, which overrides variables in the global config. @@ -372,6 +377,105 @@ nix-env -iA solc-static-versions.solc_x_y_z \ For a list of the supported `solc` versions, check [`solc-static-versions.nix`](/nix/solc-static-versions.nix). +## Package Structure and Dependency Management + +`dapp` packages have a libraries directory (by default under `lib`) where dependencies are stored as +git submodules, and a source diretory (by default under `src`) where the source code is stored. + +If you want to add a new dependency to the project you can install it to the lib dir as a submodule +using [`dapp install`](#dapp-install). Dependencies can be uninstalled using [`dapp uninstall`](#dapp-uninstall), +and upgraded to the latest version with [`dapp upgrade`](#dapp-upgrade). Running [`dapp update`](#dapp-update) +will recursively fetch all the project submodules. + +As dependencies are just git submodules under the hood you can also use the standard git tooling to +modify dependencies, e.g. check out a specific branch or commit, update the remote, or examine +the commit history. + +You can use the `DAPP_SRC` and `DAPP_LIB` variables to customize the location of the source and +library directories. + +### Import Syntax + +Projects compiled with `dapp build` can use a shortened import syntax to refer to packages in their +local `lib` directory. + +An import of the form `import "/"` will effectivley desugar to `import +"///"`, where `` is always the location of the +current packages library directory. + +To put it more concretely, consider the following project layout: + +``` +├── lib +│ ├── ds-auth +│ │ ├── lib +│ │ │ └── ds-test +│ │ │ └── src +│ │ │ └── test.sol +│ │ └── src +│ │ ├── auth.sol +│ │ └── auth.t.sol +│ │ import {DSTest} from "ds-test/test.sol" +│ └── ds-test +│ └── src +│ └── test.sol +└── src + └── test.t.sol + import {DSTest} from "ds-test/test.sol" + import {DSAuth} from "ds-auth/auth.sol" +``` + +Both `test.t.sol` and `auth.t.sol` import an instance of `DSTest` using exactly the same syntax, +however, each will resolve to a different path in the dependency tree: + +- `auth.t.sol`: resolves to `import {DSTest} from "lib/ds-auth/lib/ds-test/src/test.sol"` +- `test.t.sol`: resolves to `import {DSTest} from "lib/ds-test/src/test.sol"` + +### Deduplication + +`solc` treats identical contracts imported from different paths on disk as distinct, and this can +cause compiler errors due to name clashes when two copies of the same contract are present in an +inheritance hieracry. + +For this reason imports of contracts from identical package versions are always resolved to the same +path on disk, e.g. in the example above, if both `ds-test` packages had an identical git hash, the +imports would both resolve to `import {DSTest} from "lib/ds-test/src/test.sol"`. + +### Importing from Transitive Dependencies + +By default imports from transitive dependencies are forbidden, i.e. each package can only import +using the shortened syntax from a dependency that lives in it's local `lib` dir (standard solc +relative imports are of course still valid). + +This was however allowed in older versions, and a legacy compatibility flag is provided that should +allow compilation of older `dapp` projects that rely on this behaviour: `--allow-transitive-imports`. +This flag allows imports from transitive dependencies if there exists a single version of that +package throughout the entire dependency tree for the current package. + +As an example, consider the following project: + +``` +├── lib +│ └── ds-auth +│ ├── lib +│ │ └── ds-test +│ │ └── src +│ │ └── test.sol +│ └── src +│ ├── auth.sol +│ └── auth.t.sol +│ import {DSTest} from "ds-test/test.sol" +└── src + └── test.t.sol + import {DSTest} from "ds-test/test.sol" + import {DSAuth} from "ds-auth/auth.sol" +``` + +Compilation would fail with a plain `dapp build` as there is no `ds-test` project in the top level +`lib` directory. However if we try again with `dapp build --allow-transitive-imports`, compilation +will succeed as the import of `ds-test/test.sol` from the top level is resolved to +`lib/ds-auth/lib/ds-test/src/test.sol`. + ## Commands ### `dapp init` @@ -401,7 +505,7 @@ The compiler options of the build are generated by the [`dapp mk-standard-json`] which infers most options from the project structure. For more customizability, you can define your own configuration json by setting the file to the environment variable `DAPP_STANDARD_JSON`. -By default, `dapp build` uses [`dapp remappings`](#dapp-remappings) to resolve Solidity import paths. +By default, `dapp build` uses [`dapp remappings`](#dapp-remappings) to resolve Solidity import paths (see also [Package Structure and Dependency Management](#package-structure-and-dependency-management)). You can override this with the `DAPP_REMAPPINGS` environment variable. @@ -558,6 +662,63 @@ Requires `ETHERSCAN_API_KEY` to be set. Automatically run when the `--verify` flag is passed to `dapp create`. +### `dapp remappings` + +Generates the [solc compiler +remappings](https://docs.soliditylang.org/en/v0.8.6/path-resolution.html?highlight=remappings#import-remapping). +These resolve the dapp style imports (e.g. `import "ds-test/test.sol"`) into concrete paths on disk +(e.g. `lib/ds-test/src/test.sol`). + +Remappings are generated in the following format: `:=`, where `context` +defines the prefix of the on disk locations where the remappings will be applied, the `prefix` +defines the prefix of an import path that will be remapped, and `target` defines the result of the +remapping. + +For example a remapping of the form: `src/:ds-test/=lib/ds-test/src/` will apply to any file under +the root `src` directory, and will remap any import starting with `ds-test/` into an import starting +with `lib/ds-test/src/`. + +By default a unique set of remappings are generated for each `src` dir in the dependency tree that +remap imports into the local `lib` dir for that package. + +For example, given the following project structure: + +``` +├── lib +│ ├── ds-auth +│ │ ├── lib +│ │ │ └── ds-test +│ │ │ └── src +│ │ │ └── test.sol +│ │ └── src +│ │ ├── auth.sol +│ │ └── auth.t.sol +│ └── ds-test +│ └── src +│ └── test.sol +└── src + └── test.t.sol +``` + +`dapp remappings` would generate the following remappings: + +``` +lib/ds-auth/src/:ds-test/=lib/ds-auth/lib/ds-test/src/ +src/:ds-auth/=lib/ds-auth/src/ +src/:ds-test/=lib/ds-test/src/ +``` + +Imports to identical package versions are deduplicated to always resolve to the same on disk +location (this helps to avoid some solc compiler errors when multiple instances of the same project +are present in an inheritance hierarchy), so if both instances of `ds-test` have the same git hash, +the following remappings would be generated: + +``` +lib/ds-auth/src/:ds-test/=lib/ds-test/src/ +src/:ds-auth/=lib/ds-auth/src/ +src/:ds-test/=lib/ds-test/src/ +``` + ### `dapp mk-standard-json` Generates a Solidity settings input json using the structure of the current directory. diff --git a/src/dapp/libexec/dapp/dapp b/src/dapp/libexec/dapp/dapp index e07efbd82..427945ffa 100755 --- a/src/dapp/libexec/dapp/dapp +++ b/src/dapp/libexec/dapp/dapp @@ -8,25 +8,26 @@ ### dapp --help ### -- ### Compilation options: -### optimize activate solidity optimizer -### legacy compile using the '--combined-json' format -### extract after building, write the .abi, .bin and .bin-runtime. Implies '--legacy' +### optimize activate solidity optimizer +### legacy compile using the '--combined-json' format +### extract after building, write the .abi, .bin and .bin-runtime. Implies '--legacy' +### allow-transitive-imports allow imports with the short syntax from transitive dependencies ### ### Testing options: -### verbosity= verbosity of 'dapp test' output (0-3) -### v,verbose sets verbosity to 1 -### fuzz-runs= number of times to run fuzzing tests -### depth= number of transactions to sequence per invariant cycle -### replay= rerun a particular test case -### m,match= only run test methods matching regex -### cache= use the cache at directory -### ffi allow the use of the ffi cheatcode (WARNING: allows test authors to execute arbitrary code on your machine) -### coverage print coverage data - +### verbosity= verbosity of 'dapp test' output (0-3) +### v,verbose sets verbosity to 1 +### fuzz-runs= number of times to run fuzzing tests +### depth= number of transactions to sequence per invariant cycle +### replay= rerun a particular test case +### m,match= only run test methods matching regex +### cache= use the cache at directory +### ffi allow the use of the ffi cheatcode (WARNING: allows test authors to execute arbitrary code on your machine) +### coverage print coverage data +### ### RPC options: -### rpc fetch remote state via ETH_RPC_URL -### rpc-url= fetch remote state via -### rpc-block= block number (latest if not specified) +### rpc fetch remote state via ETH_RPC_URL +### rpc-url= fetch remote state via +### rpc-block= block number (latest if not specified) ### ### SMT options: ### smttimeout= timeout passed to the smt solver in ms (default 60000) @@ -41,62 +42,63 @@ ### async don't wait for confirmation ### Dapp testnet options: -### rpc-port=port change RPC port (default: 8545) -### rpc-addr=address change RPC address (default: 127.0.0.1) -### chain-id=number change chain ID (default: 99) -### period=seconds use a block time instead of instamine -### accounts=number create multiple accounts (default: 1) -### save=name after finishing, save snapshot -### load=name start from a previously saved snapshot -### dir=directory testnet directory +### rpc-port=port change RPC port (default: 8545) +### rpc-addr=address change RPC address (default: 127.0.0.1) +### chain-id=number change chain ID (default: 99) +### period=seconds use a block time instead of instamine +### accounts=number create multiple accounts (default: 1) +### save=name after finishing, save snapshot +### load=name start from a previously saved snapshot +### dir=directory testnet directory OPTS="dapp [] [] dapp --help -- Compilation options: -optimize activate solidity optimizer -legacy compile using the '--combined-json' format -extract after building, write the .abi, .bin and .bin-runtime. Implies '--legacy' +optimize activate solidity optimizer +legacy compile using the '--combined-json' format +extract after building, write the .abi, .bin and .bin-runtime. Implies '--legacy' +allow-transitive-imports allow imports with the short syntax from transitive dependencies Testing options: -verbosity= verbosity of 'dapp test' output (0-3) -v,verbose sets verbosity to 1 -fuzz-runs= number of times to run fuzzing tests -depth= number of transactions to sequence per invariant cycle -replay= rerun a particular test case -m,match= only run test methods matching regex -cov-match= only print coverage for files matching regex -cache= use the cache at directory -ffi allow the use of the ffi cheatcode (WARNING: allows test authors to execute arbitrary code on your machine) -coverage print coverage data +verbosity= verbosity of 'dapp test' output (0-3) +v,verbose sets verbosity to 1 +fuzz-runs= number of times to run fuzzing tests +depth= number of transactions to sequence per invariant cycle +replay= rerun a particular test case +m,match= only run test methods matching regex +cov-match= only print coverage for files matching regex +cache= use the cache at directory +ffi allow the use of the ffi cheatcode (WARNING: allows test authors to execute arbitrary code on your machine) +coverage print coverage data RPC options: -rpc fetch remote state via ETH_RPC_URL -rpc-url= fetch remote state via -rpc-block= block number (latest if not specified) +rpc fetch remote state via ETH_RPC_URL +rpc-url= fetch remote state via +rpc-block= block number (latest if not specified) SMT options: -smttimeout= timeout passed to the smt solver in ms (default 60000) -solver= name of the smt solver to use (either 'z3' or 'cvc4') -max-iterations= number of times we may revisit a particular branching point -smtdebug print the SMT queries produced by hevm +smttimeout= timeout passed to the smt solver in ms (default 60000) +solver= name of the smt solver to use (either 'z3' or 'cvc4') +max-iterations= number of times we may revisit a particular branching point +smtdebug print the SMT queries produced by hevm Deployment options: -verify verify contract on etherscan +verify verify contract on etherscan Contract verifying options: -async don't wait for confirmation +async don't wait for confirmation Dapp testnet options: -rpc-port=port change RPC port (default: 8545) -rpc-addr=address change RPC address (default: 127.0.0.1) -chain-id=number change chain ID (default: 99) -period=seconds use a block time instead of instamine -accounts=number create multiple accounts (default: 1) -save=name after finishing, save snapshot -load=name start from a previously saved snapshot -dir=directory testnet directory +rpc-port=port change RPC port (default: 8545) +rpc-addr=address change RPC address (default: 127.0.0.1) +chain-id=number change chain ID (default: 99) +period=seconds use a block time instead of instamine +accounts=number create multiple accounts (default: 1) +save=name after finishing, save snapshot +load=name start from a previously saved snapshot +dir=directory testnet directory " set -e @@ -138,45 +140,45 @@ shopt -s extglob while [[ $1 ]]; do case $1 in - --) shift; break;; - --extract) export DAPP_BUILD_EXTRACT=1;; - --optimize) export DAPP_BUILD_OPTIMIZE=1;; - --legacy) export DAPP_BUILD_LEGACY=1;; - - -m|--match) shift; export DAPP_TEST_MATCH=$1;; - -v|--verbose) export DAPP_TEST_VERBOSITY=1;; - --cov-match) shift; export DAPP_TEST_COV_MATCH=$1;; - --verbosity) shift; export DAPP_TEST_VERBOSITY=$1;; - --fuzz-runs) shift; export DAPP_TEST_FUZZ_RUNS=$1;; - --depth) shift; export DAPP_TEST_DEPTH=$1;; - - --smttimeout) shift; export DAPP_TEST_SMTTIMEOUT=$1;; - --solver) shift; export DAPP_TEST_SOLVER=$1;; - --max-iterations) shift; export DAPP_TEST_MAX_ITERATIONS=$1;; - --smtdebug) export DAPP_TEST_SMTDEBUG=1;; - --coverage) export DAPP_TEST_COVERAGE=1;; - - --replay) shift; export DAPP_TEST_REPLAY=$1;; - --cache) shift; export DAPP_TEST_CACHE=$1;; - --ffi) export DAPP_TEST_FFI=1;; - --rpc-url) shift; export HEVM_RPC=yes; export ETH_RPC_URL=$1;; - --rpc-block) shift; export HEVM_RPC=yes; export DAPP_TEST_NUMBER=$1;; - --rpc) [ -n "$ETH_RPC_URL" ] || fail "ETH_RPC_URL not set."; - export HEVM_RPC=yes;; - - --verify) export DAPP_VERIFY_CONTRACT=yes;; - - --async) export DAPP_ASYNC=yes;; - - --rpc-port) shift; export DAPP_TESTNET_RPC_PORT=$1;; - --rpc-addr) shift; export DAPP_TESTNET_RPC_ADDRESS=$1;; - --chain-id) shift; export DAPP_TESTNET_CHAINID=$1;; - --period) shift; export DAPP_TESTNET_PERIOD=$1;; - --accounts) shift; export DAPP_TESTNET_ACCOUNTS=$(($1 - 1));; - --save) shift; export DAPP_TESTNET_SAVE=$1;; - --load) shift; export DAPP_TESTNET_LOAD=$1;; - --dir) shift; export DAPP_TESTNET_gethdir=$1;; - + --) shift; break;; + --extract) export DAPP_BUILD_EXTRACT=1;; + --optimize) export DAPP_BUILD_OPTIMIZE=1;; + --legacy) export DAPP_BUILD_LEGACY=1;; + --allow-transitive-imports) export DAPP_ALLOW_TRANSITIVE_IMPORTS=1;; + + -m|--match) shift; export DAPP_TEST_MATCH=$1;; + -v|--verbose) export DAPP_TEST_VERBOSITY=1;; + --cov-match) shift; export DAPP_TEST_COV_MATCH=$1;; + --verbosity) shift; export DAPP_TEST_VERBOSITY=$1;; + --fuzz-runs) shift; export DAPP_TEST_FUZZ_RUNS=$1;; + --depth) shift; export DAPP_TEST_DEPTH=$1;; + + --smttimeout) shift; export DAPP_TEST_SMTTIMEOUT=$1;; + --solver) shift; export DAPP_TEST_SOLVER=$1;; + --max-iterations) shift; export DAPP_TEST_MAX_ITERATIONS=$1;; + --smtdebug) export DAPP_TEST_SMTDEBUG=1;; + --coverage) export DAPP_TEST_COVERAGE=1;; + + --replay) shift; export DAPP_TEST_REPLAY=$1;; + --cache) shift; export DAPP_TEST_CACHE=$1;; + --ffi) export DAPP_TEST_FFI=1;; + --rpc-url) shift; export HEVM_RPC=yes; export ETH_RPC_URL=$1;; + --rpc-block) shift; export HEVM_RPC=yes; export DAPP_TEST_NUMBER=$1;; + --rpc) [ -n "$ETH_RPC_URL" ] || fail "ETH_RPC_URL not set."; + export HEVM_RPC=yes;; + + --verify) export DAPP_VERIFY_CONTRACT=yes;; + + --async) export DAPP_ASYNC=yes;; + + --rpc-port) shift; export DAPP_TESTNET_RPC_PORT=$1;; + --rpc-addr) shift; export DAPP_TESTNET_RPC_ADDRESS=$1;; + --chain-id) shift; export DAPP_TESTNET_CHAINID=$1;; + --period) shift; export DAPP_TESTNET_PERIOD=$1;; + --accounts) shift; export DAPP_TESTNET_ACCOUNTS=$(($1 - 1));; + --save) shift; export DAPP_TESTNET_SAVE=$1;; + --load) shift; export DAPP_TESTNET_LOAD=$1;; + --dir) shift; export DAPP_TESTNET_gethdir=$1;; *) printf "${0##*/}: internal error: %q\\n" "$1"; exit 1 esac; shift diff --git a/src/dapp/libexec/dapp/dapp-remappings b/src/dapp/libexec/dapp/dapp-remappings index 30acd026c..0a1ea5a46 100755 --- a/src/dapp/libexec/dapp/dapp-remappings +++ b/src/dapp/libexec/dapp/dapp-remappings @@ -25,15 +25,16 @@ function buildDependencyTree(prefix) { // walk tree and build remappings function buildRemappings(pkg) { - const paths = mapHashes(pkg) let implicits = [] - if (process.env.DAPP_ALLOW_IMPLICIT_IMPORTS == "yes") { + if (process.env.DAPP_ALLOW_TRANSITIVE_IMPORTS == "1") { + // get the names of the direct dependencies const directs = pkg.deps.map(p => p.name) // get the transitive deps with exactly one version that are not direct deps const uniques = Object.entries(pkg.deps.reduce(buildVersionMap, {})) .filter(([name, vs]) => vs.size == 1 && !directs.includes(name)) // build remappings that allow importing these deps from `pkg` + const paths = mapHashes(pkg) implicits = uniques.map(([name, v]) => `${pkg.path}/:${name}/=${paths[([...v][0])]}/`) } @@ -67,9 +68,7 @@ function buildVersionMap(map, pkg) { return map } - return pkg.deps.reduce((versions, dep) => { - return update(versions, dep) - }, update(map, pkg)) + return pkg.deps.reduce(update, update(map, pkg)) } // strip the leading `.` or `./` from a path From c01b0b52587c8964d0ff2b774cfb9b233d9dc582 Mon Sep 17 00:00:00 2001 From: David Terry Date: Thu, 5 Aug 2021 14:08:49 +0200 Subject: [PATCH 7/9] dapp: remappings: fix transitive-imports flag --- src/dapp/libexec/dapp/dapp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dapp/libexec/dapp/dapp b/src/dapp/libexec/dapp/dapp index 427945ffa..a0bc47420 100755 --- a/src/dapp/libexec/dapp/dapp +++ b/src/dapp/libexec/dapp/dapp @@ -111,13 +111,6 @@ if ! [[ $DAPP_INIT ]]; then [[ $(pwd) != ~ && -e .dapprc ]] && . .dapprc fi -export DAPP_SRC=${DAPP_SRC-src} -export DAPP_LIB=${DAPP_LIB-lib} -export DAPP_OUT=${DAPP_OUT-out} -export DAPP_JSON=${DAPP_JSON-${DAPP_OUT}/dapp.sol.json} -export DAPP_ROOT=${DAPP_ROOT-.} -export DAPP_REMAPPINGS=${DAPP_REMAPPINGS-"$(dapp-remappings)"} - if [[ $2 = --help ]]; then exec "${0##*/}" help -- "$1" elif [[ $DAPP_SOLC_VERSION ]]; then @@ -184,6 +177,13 @@ while [[ $1 ]]; do esac; shift done +export DAPP_SRC=${DAPP_SRC-src} +export DAPP_LIB=${DAPP_LIB-lib} +export DAPP_OUT=${DAPP_OUT-out} +export DAPP_JSON=${DAPP_JSON-${DAPP_OUT}/dapp.sol.json} +export DAPP_ROOT=${DAPP_ROOT-.} +export DAPP_REMAPPINGS=${DAPP_REMAPPINGS-"$(dapp-remappings)"} + if ! [ -x "$(command -v "${0##*/}-${1-help}")" ]; then # look for approximate matches echo >&2 "'$1' is not a dapp command. See 'dapp help'." From e42da31638e8626d0d719eed8b20b79d49245f41 Mon Sep 17 00:00:00 2001 From: David Terry Date: Wed, 8 Sep 2021 18:06:53 +0200 Subject: [PATCH 8/9] dapp: remappings: use shortest path when deduplicating --- src/dapp/libexec/dapp/dapp-remappings | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dapp/libexec/dapp/dapp-remappings b/src/dapp/libexec/dapp/dapp-remappings index 0a1ea5a46..f1b16d390 100755 --- a/src/dapp/libexec/dapp/dapp-remappings +++ b/src/dapp/libexec/dapp/dapp-remappings @@ -52,7 +52,11 @@ function deduplicate(pkg) { // walk tree and build a mapping from hash => path function mapHashes(pkg) { const go = (mapping, dep) => { - mapping[dep.hash] = dep.path + // we collect the shortest path (aka closest to the root) for each dep + if (mapping[dep.hash] == undefined || dep.path.length < mapping[dep.hash].length) { + mapping[dep.hash] = dep.path + } + return dep.deps.reduce(go, mapping) } return pkg.deps.reduce(go, { [pkg.hash]: pkg.path }) From b9e5d9272295e155b05164d465d36474362ea162 Mon Sep 17 00:00:00 2001 From: David Terry Date: Wed, 8 Sep 2021 18:07:09 +0200 Subject: [PATCH 9/9] dapp: remappings: normalize DAPP_SRC and DAPP_LIB --- src/dapp/libexec/dapp/dapp-remappings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dapp/libexec/dapp/dapp-remappings b/src/dapp/libexec/dapp/dapp-remappings index f1b16d390..f1ccab510 100755 --- a/src/dapp/libexec/dapp/dapp-remappings +++ b/src/dapp/libexec/dapp/dapp-remappings @@ -14,10 +14,10 @@ console.log(buildRemappings(deduplicate(buildDependencyTree("."))).join("\n")) // deps: [] // } function buildDependencyTree(prefix) { - const lib = `${prefix}/${process.env.DAPP_LIB}` + const lib = `${prefix}/${normalize(process.env.DAPP_LIB)}` return { name: prefix.split("/").pop(), - path: normalize(`${prefix}/${process.env.DAPP_SRC}`), + path: normalize(`${prefix}/${normalize(process.env.DAPP_SRC)}`), hash: hash(prefix), deps: ls(lib).map(p => buildDependencyTree(`${lib}/${p}`)) }