Commit graph

283 commits

Author SHA1 Message Date
Wladimir J. van der Laan 028b0d963c
Merge #13425: Moving final scriptSig construction from CombineSignatures to ProduceSignature (PSBT signer logic)
b81560029 Remove CombineSignatures and replace tests (Andrew Chow)
ed94c8b55 Replace CombineSignatures with ProduceSignature (Andrew Chow)
0422beb9b Make SignatureData able to store signatures and scripts (Andrew Chow)
b6edb4f5e Inline Sign1 and SignN (Andrew Chow)

Pull request description:

  Currently CombineSignatures is used to create the final scriptSig or an input. However ProduceSignature is capable of doing this itself. Using both CombineSignatures and ProduceSignature results in code duplication which is unnecessary.

  To move the scriptSig construction to ProduceSignatures, the SignatureData class contains two maps to hold pubkeys mapped to signatures, and script ids mapped to scripts. DataFromTransaction is extended to be able to extract signatures, their public keys, and scripts from existing ScriptSigs.

  The SignaureData are then passed down to SignStep which can use the aforementioned maps to get the signatures, pubkeys, and scripts that it needs, falling back to the actual SigningProvider and SignatureCreator if the data are not available in the SignatureData.

  Additionally, Sign1 and SignN have been removed and their functionality inlined into SignStep since Sign1 is really just a wrapper around CreateSig.

  Since ProduceSignature can produce the final scriptSig or scriptWitness by using SignatureData which has extracted data from the transaction, CombineSignatures is unnecessary as ProduceSignature is able to replicate all of CombineSignatures' functionality.

  This also furthers BIP 174 support and begins moving towards a BIP 174 style backend.

  The tests have also been updated to use the new combining methodology.

Tree-SHA512: 78cd58a4ebe37f79229bd5eee2958a0bb45cd7f36d0e993eee13ff685b3665dd76ef2dfd5f47d34678995bb587f5594100ee5f6c09b1c69ee96d3684d470d01e
2018-07-05 17:18:50 +02:00
Andrew Chow b815600295 Remove CombineSignatures and replace tests
Removes CombineSignatures and replaces its use in tests with
ProduceSignature to test the same behavior for ProduceSignature.
2018-07-03 17:19:23 -07:00
Andrew Chow ed94c8b556 Replace CombineSignatures with ProduceSignature
Instead of using CombineSignatures to create the final scriptSig or
scriptWitness of an input, use ProduceSignature itself.

To allow for ProduceSignature to place signatures, pubkeys, and scripts
that it does not know about, we pass down the SignatureData to SignStep
which pulls out the information that it needs from the SignatureData.
2018-07-03 17:19:23 -07:00
Andrew Chow 0422beb9bd Make SignatureData able to store signatures and scripts
In addition to having the scriptSig and scriptWitness, have SignatureData
also be able to store just the signatures (pubkeys mapped to sigs) and
scripts (script ids mapped to scripts).

Also have DataFromTransaction be able to extract signatures and scripts
from the scriptSig and scriptWitness of an input to put them in SignatureData.

Adds a new SignatureChecker which takes a SignatureData and puts pubkeys
and signatures into it when it successfully verifies a signature.

Adds a new field in SignatureData which stores whether the SignatureData
was complete. This allows us to also update the scriptSig and
scriptWitness to the final one when updating a SignatureData with another
one.
2018-07-03 17:18:52 -07:00
Andrew Chow b6edb4f5e6 Inline Sign1 and SignN
Sign1 and SignN are kind of redundant so remove them and inline their
behavior into SignStep
2018-06-29 17:32:32 -07:00
Pieter Wuille bb582a59c7 Add P2WSH destination helper and use it instead of manual hashing 2018-06-17 19:44:50 -07:00
Pieter Wuille e6b9730c49 Do not expose invalidity from IsMine 2018-06-17 16:28:04 -07:00
Wladimir J. van der Laan f0fd39f376
Merge #13269: refactoring: Drop UpdateTransaction in favor of UpdateInput
6aa33feadb Drop UpdateTransaction in favor of UpdateInput (Ben Woosley)

Pull request description:

  Updating the input explicitly requires the caller to present a mutable
  input, which more clearly communicates the effects and intent of the call
  (and, often, the enclosing loop).

  In most cases, this input is already immediately available and need not be
  looked up.

Tree-SHA512: 8c7914a8b7ae975d8ad0e9d760e3c5da65776a5f79d060b8ffb6b3ff7a32235f71ad705f2185b368d9263742d7796bb562395d22b806d90e8502d8c496011e57
2018-06-05 19:06:16 +02:00
MarcoFalke 2140f6cbc5
Merge #13351: wallet: Prevent segfault when sending to unspendable witness
fa36aa7965 wallet: Prevent segfault when sending to unspendable witness (MarcoFalke)

Pull request description:

  Previously we wouldn't care about the `txnouttype`, but after 4e91820531 we `switch` on the type.

Tree-SHA512: 6b597aba80cb43881671ad7b3a4ad97753864e8005a05c23fdd8ee79953483c08f241b5c392a9b494298eadc5cfba895b0480d916ef4f11d122fd6196f31b84a
2018-06-05 11:38:09 -04:00
Wladimir J. van der Laan 36fc8052f6
Merge #13309: Directly operate with CMutableTransaction in SignSignature
6b8b63af14 Generic TransactionSignatureCreator works with both CTransaction and CMutableTransaction (Martin Ankerl)

Pull request description:

  Refactored `TransactionSignatureCreator` into a templated `GenericTransactionSignatureCreator` that works with both `CMutableTransaction` and `CTransaction`.

  The advantage is that now in `SignSignature`, the `MutableTransactionSignatureCreator` can now operate directly with the `CMutableTransaction` without the need to copy the data into a `CTransaction`.

  Running all unit tests brings a very noticable speedup on my machine:

      48.4 sec before this change
      36.4 sec with this change
      --------
      12.0 seconds saved

  running only `--run_test=transaction_tests/test_big_witness_transaction`:

      16.7 sec before this change
       5.9 sec with this change
      --------
      10.8 seconds saved

  This relates to my first attempt with the const_cast hack #13202, and to the slow unit test issue #10026.

  Also see #13050 which modifies the tests but not the production code (like this PR) to get a speedup.

Tree-SHA512: 2cff0e9699f484f26120a40e431a24c8bc8f9e780fd89cb0ecf20c5be3eab6c43f9c359cde244abd9f3620d06c7c354e3b9dd3da41fa2ca1ac1e09386fea25fb
2018-05-31 10:40:11 +02:00
MarcoFalke fa36aa7965
wallet: Prevent segfault when sending to unspendable witness 2018-05-30 15:46:06 -04:00
Wladimir J. van der Laan fd96d54f39
Merge #13194: Remove template matching and pseudo opcodes
c814e2e7e8 Remove template matching and pseudo opcodes (Pieter Wuille)

Pull request description:

  The current code contains a rather complex script template matching engine, which is only used for 3 particular script types (P2PK, P2PKH, multisig). The first two of these are trivial to match for otherwise, and a specialized matcher for multisig is both more compact and more efficient than a generic one.

  The goal is being more flexible, so that for example larger standard multisigs inside SegWit outputs are easier to implement.

  As a side-effect, it also gets rid of the pseudo opcodes hack.

Tree-SHA512: 643b409c5c36821519f613a43efd399af0ec99b6131f35cd4024decfb2d483d719e0e921cd088bc9832a7ac797cb4a6b1158b8574c82f7fbebb75f1b31b359df
2018-05-30 16:50:43 +02:00
Martin Ankerl 6b8b63af14 Generic TransactionSignatureCreator works with both CTransaction and CMutableTransaction
Templated version so that no copying of CMutableTransaction into a CTransaction is
necessary. This speeds up the test case transaction_tests/test_big_witness_transaction
from 7.9 seconds to 3.1 seconds on my machine.
2018-05-30 16:01:36 +02:00
Pieter Wuille c814e2e7e8 Remove template matching and pseudo opcodes
The current code contains a rather complex script template matching engine,
which is only used for 3 particular script types (P2PK, P2PKH, multisig).
The first two of these are trivial to match for otherwise, and a specialized
matcher for multisig is both more compact and more efficient than a generic
one.

The goal is being more flexible, so that for example larger standard multisigs
inside SegWit outputs are more easy to implement.

As a side-effect, it also gets rid of the pseudo opcodes hack.
2018-05-29 14:40:18 -07:00
Wladimir J. van der Laan 56fe3dc235
Merge #13142: Separate IsMine from solvability
c004ffc9b4 Make handling of invalid in IsMine more uniform (Pieter Wuille)
a53f0feff8 Add some checks for invalid recursion in IsMine (Pieter Wuille)
b5802a9f5f Simplify IsMine logic (Pieter Wuille)
4e91820531 Make IsMine stop distinguishing solvable/unsolvable (Pieter Wuille)
6d714c3419 Make coincontrol use IsSolvable to determine solvability (Pieter Wuille)

Pull request description:

  Our current `IsMine` logic does several things with outputs:
  * Determine "spendability" (roughly corresponding to "could we sign for this")
  * Determine "watching" (is this an output directly or indirectly a watched script)
  * Determine invalidity (is this output definitely not legally spendable, detecting accidental uncompressed pubkeys in witnesses)
  * Determine "solvability" (would we be able to sign for this ignoring the fact that we may be missing some private keys).

  The last item (solvability) is mostly unrelated and only rarely needed (there is just one instance, inside the wallet's coin control logic). This PR changes that instance to use the separate `IsSolvable` function, and stop `IsMine` from distinguishing between solvable and unsolvable.

  As an extra, this also simplifies the `IsMine` logic and adds some extra checks (which wouldn't be hit unless someone adds already invalid scripts to their wallet).

Tree-SHA512: 95a6ef75fbf2eedc5ed938c48a8e5d77dcf09c933372acdd0333129fb7301994a78498f9aacce2c8db74275e19260549dd67a83738e187d40b5090cc04f33adf
2018-05-29 15:12:16 +02:00
Pieter Wuille c004ffc9b4 Make handling of invalid in IsMine more uniform 2018-05-24 10:29:02 -07:00
Ben Woosley 6aa33feadb
Drop UpdateTransaction in favor of UpdateInput
Updating the input explicitly requires the caller to present a mutable
input, which more clearly communicates the effects and intent of the method.

In most cases, this input is already immediately available and need not be
looked up.
2018-05-18 11:08:13 -07:00
Johnson Lau 9dabfe49c0 Add constant scriptCode policy in non-segwit scripts
This disables OP_CODESEPARATOR in non-segwit scripts (even in an unexecuted branch), and makes a positive FindAndDelete result invalid. This ensures that the scriptCode serialized in SignatureHash() is always the same as the script passing to the EvalScript.
2018-05-05 04:26:12 +08:00
Pieter Wuille a53f0feff8 Add some checks for invalid recursion in IsMine 2018-05-03 11:02:01 -07:00
Pieter Wuille b5802a9f5f Simplify IsMine logic 2018-05-03 11:02:01 -07:00
Pieter Wuille 4e91820531 Make IsMine stop distinguishing solvable/unsolvable 2018-05-03 11:02:01 -07:00
Wladimir J. van der Laan 487dcbe80c
Merge #13002: Do not treat bare multisig outputs as IsMine unless watched
7d0f80b Use anonymous namespace instead of static functions (Pieter Wuille)
b61fb71 Mention removal of bare multisig IsMine in release notes (Pieter Wuille)
9c2a8b8 Do not treat bare multisig as IsMine (Pieter Wuille)
08f3228 Optimization: only test for witness scripts at top level (Pieter Wuille)
3619735 Track difference between scriptPubKey and P2SH execution in IsMine (Pieter Wuille)
ac6ec62 Switch to a private version of SigVersion inside IsMine (Pieter Wuille)
19fc973 Do not expose SigVersion argument to IsMine (Pieter Wuille)
fb1dfbb Remove unused IsMine overload (Pieter Wuille)
952d821 Make CScript -> CScriptID conversion explicit (Pieter Wuille)

Pull request description:

  Currently our wallet code will treat bare multisig outputs (meaning scriptPubKeys with multiple public keys + `OP_CHECKMULTISIG` operator in it) as ours without the user asking for it, as long as all private keys in it are in our wallet.

  This is a pointless feature. As it only works when all private keys are in one place, it's useless compared to single key outputs (P2PK, P2PKH, P2WPKH, P2SH-P2WPKH), and worse in terms of space, cost, UTXO size, and ability to test (due to lack of address format for them).

  Furthermore, they are problematic in that producing a list of all `scriptPubKeys` we accept is not tractable (it involves all combinations of all public keys that are ours). In further wallet changes I'd like to move to a model where all scriptPubKeys that are treated as ours are explicit, rather than defined by whatever keys we have. The current behavior of the wallet is very hard to model in such a design, so I'd like to get rid of it.

  I think there are two options:
  * Remove it entirely (do not ever accept bare multisig outputs as ours, unless watched)
  * Only accept bare multisig outputs in situations where the P2SH version of that output would also be acceptable

  This PR implements the first option. The second option was explored in #12874.

Tree-SHA512: 917ed45b3cac864cee53e27f9a3e900390c576277fbd6751b1250becea04d692b3b426fa09065a3399931013bd579c4f3dbeeb29d51d19ed0c64da75d430ad9a
2018-04-26 20:10:12 +02:00
Pieter Wuille 7d0f80bbf4 Use anonymous namespace instead of static functions 2018-04-19 21:06:47 -07:00
Pieter Wuille 9c2a8b8d34 Do not treat bare multisig as IsMine
Such outputs can still be watched, and signed for, but they aren't treated as valid payments.
That means they won't cause transactions to appear in listtransactions, their outputs to be
shown under listunspent, or affect balances.
2018-04-19 21:05:14 -07:00
Pieter Wuille 08f3228654 Optimization: only test for witness scripts at top level
Inside P2SH scripts we already know that the P2SH script version of witness keys/scripts
are acceptable, so there is no need to test for it again.
2018-04-19 21:05:14 -07:00
Pieter Wuille 3619735b09 Track difference between scriptPubKey and P2SH execution in IsMine
Inside IsMine we care about the distinction between scriptPubKey execution
and P2SH redeemScript execution. The consensus code does not care about this
distinction, and thus SigVersion does not have a field for P2SH. As the IsMine
code will care, it uses a separate enum with more fields.
2018-04-19 21:05:04 -07:00
Pieter Wuille ac6ec62522 Switch to a private version of SigVersion inside IsMine
This will allow us to have the consensus code and IsMine code diverge.
2018-04-19 20:53:18 -07:00
Pieter Wuille 19fc973097 Do not expose SigVersion argument to IsMine
Only IsMine's internal code needs this, as part of a recursion into P2SH and P2WSH
scripts. The exposed functions always operate on actual scriptPubKeys and not on
redeemScripts or witness scripts.
2018-04-19 20:52:47 -07:00
Pieter Wuille fb1dfbbec0 Remove unused IsMine overload 2018-04-18 17:08:53 -07:00
Pieter Wuille 952d8213a6 Make CScript -> CScriptID conversion explicit 2018-04-18 17:08:50 -07:00
Pieter Wuille 54a5a21158 [MOVEONLY] Turn CScript::GetOp2 into a function and move to cpp 2018-04-13 09:07:29 -07:00
Pieter Wuille 6a7456ad60 [MOVEONLY] Move CSCript::FindAndDelete to interpreter 2018-04-13 09:07:27 -07:00
Pieter Wuille 33a8ecfbce Delete unused non-const-iterator CSCript::GetOp overloads 2018-04-13 09:06:35 -07:00
Pieter Wuille 2fb168b55d Make iterators in CScript::FindAndDelete const 2018-04-13 09:06:35 -07:00
Ben Woosley 8cbc5c4be4
Drop dead code CScript::Find
Last use removed in 922e8e2929 (2012!)
2018-04-12 18:57:23 -07:00
Wladimir J. van der Laan 8480d41e0f
Merge #12803: Make BaseSignatureCreator a pure interface
be67831 Make DummySignatureCreator a singleton (Pieter Wuille)
190b8d2 Make BaseSignatureCreator a pure interface (Pieter Wuille)

Pull request description:

  * Removes the `m_provider` field from `BaseSignatureCreator`. Instead both a `SigningProvider` (which provides keys and scripts) and a `BaseSignatureCreator` (which implements the transaction-specific (or other) signing logic) are passed into and down in `ProduceSignature`, making the two concepts orthogonal.
  * Makes `BaseSignatureCreator` a pure interface without constructor, making it easier to implement new derivations of it (for example for message signing).
  * As `DummySignatureCreator` now becomes a stateless object, turn it into a singleton `DUMMY_SIGNATURE_CREATOR`.

Tree-SHA512: 5f1f4512e4ea7d02a31df7b9ede55008efa716c5b74a2630ca1c2fc6599584d8bf5f5641487266127f4b3788033803539fbd22b03ef1219c83c10da2d3da3dcd
2018-04-12 22:55:56 +02:00
Ben Woosley 3450a9b25c
Extract consts for WITNESS_V0 hash sizes 2018-04-10 20:13:32 -07:00
Wladimir J. van der Laan 0a8054e7cd
Merge #12731: Support serialization as another type without casting
818dc74 Support serialization as another type without casting (Pieter Wuille)

Pull request description:

  This adds a `READWRITEAS(type, obj)` macro which serializes `obj` as if it were converted to `const type&` when `const`, and to `type&` when non-`const`. No actual cast is involved, so this only works when this conversion can be done automatically.

  This makes it usable in serialization code that uses a single implementation for both serialization and deserializing, which doesn't know the constness of the object involved.

  This is a redo of #12712, using a slightly different interface.

Tree-SHA512: 262f0257284ff99b5ffaec9b997c194e221522ba35c3ac8eaa9bb344449d7ea0a314de254dc77449fa7aaa600f8cd9a24da65aade8c1ec6aa80c6e9a7bba5ca7
2018-04-10 20:54:33 +02:00
Pieter Wuille be67831210 Make DummySignatureCreator a singleton 2018-04-10 09:29:17 -07:00
Pieter Wuille 190b8d2dcf Make BaseSignatureCreator a pure interface 2018-04-10 09:29:17 -07:00
Pieter Wuille 88430cbab4
Merge #12167: Make segwit failure due to CLEANSTACK violation return a SCRIPT_ERR_CLEANSTACK error code
1e747e3c1e Make segwit failure due to CLEANSTACK violation return a SCRIPT_ERR_CLEANSTACK error code. (Mark Friedenbach)

Pull request description:

  If a segwit script terminates with a stack size not equal to one, the current error code is EVAL_FALSE. This is semantically wrong, and prevents explicitly checking CLEANSTACK violations in the unit tests. This PR changes the error code (and affected unit tests) to use SCRIPT_ERROR_CLEANSTACK instead of SCRIPT_ERROR_EVAL_FALSE.

Tree-SHA512: 8f7b1650f7a23a942cde1070e3e56420be456b4a7be42515b237e95557bf2bd5e7ba9aabd213c8092bea28c165dbe73f5a3486300089aeb01e698151b42484b1
2018-04-04 17:30:24 -07:00
MarcoFalke bfaed1ab2e
Merge #12460: Assert CPubKey::ValidLength to the pubkey's header-relevant size
f8c249ab91 Assert CPubKey::ValidLength to the pubkey's header-relevent size (Ben Woosley)

Pull request description:

  A pubkey's length is specific to its type which is indicated by its header value. GetLen returns the header-indicated length, so this change ensures that a key matches its header-indicated length.

  And replace some magic values with their constant equivalents.

Tree-SHA512: b727b39a631babe0932326396fc4d796ade8ec1e37454ff0c709ae9b78ecbd0cfdf59d84089ba8415e6efa7bc180e3cd39a14ddaf0871cbac54b96851e1b7b44
2018-04-04 17:50:32 -04:00
MarcoFalke 0c5f67b8e5
Merge #12757: Clarify include guard naming convention
3bcc0059b8 Add lint-include-guards.sh which checks include guard consistency (practicalswift)
8fd6af89a0 Fix missing or inconsistent include guards (practicalswift)
8af65d96f4 Document include guard convention (practicalswift)

Pull request description:

  * **Documentation**: Document include guard convention
  * **Fix**: Fix missing or inconsistent include guards
  * **Regression test**: Add `lint-include-guards.sh` which checks include guard consistency

Tree-SHA512: 8171878f60fd08ccbea943a11e835195750592abb9d7ab74eaa4265ae7fac523b1da9d31ca13d6ab73dd596e49986bfb7593c696e5f39567c93e610165bc2acc
2018-04-01 18:30:21 -04:00
Wladimir J. van der Laan bf8fc7d352
Merge #12714: Introduce interface for signing providers
d40f06a Introduce interface for signing providers (Pieter Wuille)

Pull request description:

  `CKeyStore` is a rich interface that provides many features, including knowledge of scripts and pubkeys for solving, private keys for signing, in addition to watch-only keys and scripts, and distinguishing lack of keys from them just being encrypted.

  The signing logic in script/sign does not actually need most of these features. Here we introduce a simpler interface (`SigningProvider`) which *only* provides keys and scripts. This is actually sufficient for signing.

  In addtion, we swap the dependency between keystore and script/sign (keystore now depends on script/script with `CKeyStore` deriving from `SigningProvider`, rather than `CKeyStore` being the interface that signing relies on).

  This is a very early step towards the design in https://gist.github.com/sipa/125cfa1615946d0c3f3eec2ad7f250a2, separating the concern between deciding what outputs are ours and signing.

Tree-SHA512: d511b7b03eec0e513530db1d9ae5aacf6d0bfa1d3e1c03d06c5bde396bafb5824c4491b227d32bcda9288530caf49835da18e846ccf66538d6c0cc6ae27291c9
2018-03-27 20:20:55 +02:00
Wladimir J. van der Laan 3de01268b7
Merge #10742: scripted-diff: Use scoped enumerations (C++11, "enum class")
1f45e21 scripted-diff: Convert 11 enums into scoped enums (C++11) (practicalswift)

Pull request description:

  Rationale (from Bjarne Stroustrup's ["C++11 FAQ"](http://www.stroustrup.com/C++11FAQ.html#enum)):

  >
  > The enum classes ("new enums", "strong enums") address three problems with traditional C++ enumerations:
  >
  > * conventional enums implicitly convert to int, causing errors when someone does not want an enumeration to act as an integer.
  > * conventional enums export their enumerators to the surrounding scope, causing name clashes.
  > * the underlying type of an enum cannot be specified, causing confusion, compatibility problems, and makes forward declaration impossible.
  >
  > The new enums are "enum class" because they combine aspects of traditional enumerations (names values) with aspects of classes (scoped members and absence of conversions).

Tree-SHA512: 9656e1cf4c3cabd4378c7a38d0c2eaf79e4a54d204a3c5762330840e55ee7e141e188a3efb2b4daf0ef3110bbaff80d8b9253abf2a9b015cdc4d60b49ac2b914
2018-03-27 16:38:14 +02:00
Pieter Wuille d40f06a3da Introduce interface for signing providers
CKeyStore is a rich interface that provides many features, including knowledge
of scripts and pubkeys for solving, private keys for signing, in addition to
watch-only keys and scripts, and distinguishing lack of keys from them just
being encrypted.

The signing logic in script/sign does not actually need most of these features.
Here we introduce a simpler interface (SigningProvider) which *only* provides
keys and scripts. This is actually sufficient for signing.

In addtion, we swap the dependency between keystore and script/sign
(keystore now depends on script/script with CKeyStore deriving from
SigningProvider, rather than CKeyStore being the interface that signing
relies on).
2018-03-25 13:26:57 -07:00
practicalswift 8fd6af89a0 Fix missing or inconsistent include guards 2018-03-22 16:23:04 +01:00
Dimitris Apostolou 4d9b4256d8 Fix typos 2018-03-21 08:34:44 +02:00
Pieter Wuille 818dc74ba2 Support serialization as another type without casting
This adds a READWRITEAS(type, obj) macro which serializes obj as if it
were casted to (const type&) when const, and to (type&) when non-const.

This makes it usable in serialization code that uses a single
implementation for both serialization and deserializing, which doesn't
know the constness of the object involved.
2018-03-20 17:08:06 -07:00
Pieter Wuille 7be9a9a570
Merge #12683: Fix more constness violations in serialization code
172f5fa738 Support deserializing into temporaries (Pieter Wuille)
2761bca997 Merge READWRITEMANY into READWRITE (Pieter Wuille)

Pull request description:

  This is another fragment of improvements from #10785.

  The current serialization code does not support serializing/deserializing from/to temporaries (like `s >> CFlatData(script)`). As a result, there are many invocations of the `REF` macro which in addition to changing the reference type also changes the constness. This is unnecessary in C++11 as we can use rvalue references now instead.

  The first commit is an extra simplification we can make that removes the duplication of code between `READWRITE` and `READWRITEMANY` (and related functions).

Tree-SHA512: babfa9cb268cc3bc39917e4f0a90e4651c33d85032161e16547a07f3b257b7ca7940e0cbfd69f09439d26fafbb1a6cf6359101043407e2c7aeececf7f20b6eed
2018-03-15 16:57:55 -07:00