Skip to content

Commit 586da80

Browse files
hewigovensvikmeup
authored andcommitted
Zcash Blossom support (#775)
* zcash blossom support * Add branch id to proto and swift test
1 parent fc5b1c8 commit 586da80

20 files changed

+236
-88
lines changed

src/Bitcoin/TransactionPlan.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,18 @@ struct TransactionPlan {
2828
/// Selected unspent transaction outputs.
2929
std::vector<Bitcoin::Proto::UnspentTransaction> utxos;
3030

31+
/// Zcash branch id
32+
std::vector<uint8_t> branchId;
33+
3134
TransactionPlan() = default;
3235

3336
TransactionPlan(const Proto::TransactionPlan& plan)
3437
: amount(plan.amount())
3538
, availableAmount(plan.available_amount())
3639
, fee(plan.fee())
3740
, change(plan.change())
38-
, utxos(plan.utxos().begin(), plan.utxos().end()) {}
41+
, utxos(plan.utxos().begin(), plan.utxos().end())
42+
, branchId(plan.branch_id().begin(), plan.branch_id().end()) {}
3943

4044
Proto::TransactionPlan proto() const {
4145
auto plan = Proto::TransactionPlan();
@@ -44,6 +48,7 @@ struct TransactionPlan {
4448
plan.set_fee(fee);
4549
plan.set_change(change);
4650
*plan.mutable_utxos() = {utxos.begin(), utxos.end()};
51+
plan.set_branch_id(branchId.data(), branchId.size());
4752
return plan;
4853
}
4954
};

src/Bitcoin/TransactionSigner.cpp

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
using namespace TW;
2020
using namespace TW::Bitcoin;
2121

22-
template <typename Transaction>
23-
Result<Transaction> TransactionSigner<Transaction>::sign() {
22+
template <typename Transaction, typename TransactionBuilder>
23+
Result<Transaction> TransactionSigner<Transaction, TransactionBuilder>::sign() {
2424
signedInputs.clear();
2525
std::copy(std::begin(transaction.inputs), std::end(transaction.inputs),
2626
std::back_inserter(signedInputs));
@@ -47,8 +47,8 @@ Result<Transaction> TransactionSigner<Transaction>::sign() {
4747
return Result<Transaction>::success(std::move(tx));
4848
}
4949

50-
template <typename Transaction>
51-
Result<void> TransactionSigner<Transaction>::sign(Script script, size_t index,
50+
template <typename Transaction, typename TransactionBuilder>
51+
Result<void> TransactionSigner<Transaction, TransactionBuilder>::sign(Script script, size_t index,
5252
const Bitcoin::Proto::UnspentTransaction& utxo) {
5353
Script redeemScript;
5454
std::vector<Data> results;
@@ -116,8 +116,8 @@ Result<void> TransactionSigner<Transaction>::sign(Script script, size_t index,
116116
return Result<void>::success();
117117
}
118118

119-
template <typename Transaction>
120-
Result<std::vector<Data>> TransactionSigner<Transaction>::signStep(
119+
template <typename Transaction, typename TransactionBuilder>
120+
Result<std::vector<Data>> TransactionSigner<Transaction, TransactionBuilder>::signStep(
121121
Script script, size_t index, const Bitcoin::Proto::UnspentTransaction& utxo, uint32_t version) {
122122
Transaction transactionToSign(transaction);
123123
transactionToSign.inputs = signedInputs;
@@ -204,8 +204,8 @@ Result<std::vector<Data>> TransactionSigner<Transaction>::signStep(
204204
}
205205
}
206206

207-
template <typename Transaction>
208-
Data TransactionSigner<Transaction>::createSignature(const Transaction& transaction,
207+
template <typename Transaction, typename TransactionBuilder>
208+
Data TransactionSigner<Transaction, TransactionBuilder>::createSignature(const Transaction& transaction,
209209
const Script& script, const Data& key,
210210
size_t index, Amount amount,
211211
uint32_t version) {
@@ -220,8 +220,8 @@ Data TransactionSigner<Transaction>::createSignature(const Transaction& transact
220220
return sig;
221221
}
222222

223-
template <typename Transaction>
224-
Data TransactionSigner<Transaction>::pushAll(const std::vector<Data>& results) {
223+
template <typename Transaction, typename TransactionBuilder>
224+
Data TransactionSigner<Transaction, TransactionBuilder>::pushAll(const std::vector<Data>& results) {
225225
auto data = Data{};
226226
for (auto& result : results) {
227227
if (result.empty()) {
@@ -245,8 +245,8 @@ Data TransactionSigner<Transaction>::pushAll(const std::vector<Data>& results) {
245245
return data;
246246
}
247247

248-
template <typename Transaction>
249-
Data TransactionSigner<Transaction>::keyForPublicKeyHash(const Data& hash) const {
248+
template <typename Transaction, typename TransactionBuilder>
249+
Data TransactionSigner<Transaction, TransactionBuilder>::keyForPublicKeyHash(const Data& hash) const {
250250
for (auto& key : input.private_key()) {
251251
auto publicKey = PrivateKey(key).getPublicKey(TWPublicKeyTypeSECP256k1);
252252
auto keyHash = TW::Hash::ripemd(TW::Hash::sha256(publicKey.bytes));
@@ -257,8 +257,8 @@ Data TransactionSigner<Transaction>::keyForPublicKeyHash(const Data& hash) const
257257
return {};
258258
}
259259

260-
template <typename Transaction>
261-
Data TransactionSigner<Transaction>::scriptForScriptHash(const Data& hash) const {
260+
template <typename Transaction, typename TransactionBuilder>
261+
Data TransactionSigner<Transaction, TransactionBuilder>::scriptForScriptHash(const Data& hash) const {
262262
auto hashString = hex(hash.begin(), hash.end());
263263
auto it = input.scripts().find(hashString);
264264
if (it == input.scripts().end()) {
@@ -269,6 +269,6 @@ Data TransactionSigner<Transaction>::scriptForScriptHash(const Data& hash) const
269269
}
270270

271271
// Explicitly instantiate a Signers for compatible transactions.
272-
template class TW::Bitcoin::TransactionSigner<Bitcoin::Transaction>;
273-
template class TW::Bitcoin::TransactionSigner<Zcash::Transaction>;
274-
template class TW::Bitcoin::TransactionSigner<Groestlcoin::Transaction>;
272+
template class TW::Bitcoin::TransactionSigner<Bitcoin::Transaction, Bitcoin::TransactionBuilder>;
273+
template class TW::Bitcoin::TransactionSigner<Zcash::Transaction, Zcash::TransactionBuilder>;
274+
template class TW::Bitcoin::TransactionSigner<Groestlcoin::Transaction, Bitcoin::TransactionBuilder>;

src/Bitcoin/TransactionSigner.h

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "../PrivateKey.h"
1717
#include "../Result.h"
1818
#include "../Zcash/Transaction.h"
19+
#include "../Zcash/TransactionBuilder.h"
1920
#include "../proto/Bitcoin.pb.h"
2021

2122
#include <memory>
@@ -25,7 +26,7 @@
2526
namespace TW::Bitcoin {
2627

2728
/// Helper class that performs Bitcoin transaction signing.
28-
template <typename Transaction>
29+
template <typename Transaction, typename TransactionBuilder>
2930
class TransactionSigner {
3031
private:
3132
/// Private key and redeem script provider for signing.
@@ -44,17 +45,17 @@ class TransactionSigner {
4445

4546
public:
4647
/// Initializes a transaction signer with signing input.
47-
TransactionSigner(Bitcoin::Proto::SigningInput &&input)
48+
TransactionSigner(Bitcoin::Proto::SigningInput&& input)
4849
: input(input), plan(TransactionBuilder::plan(input)) {
49-
transaction = TransactionBuilder::build<Transaction>(
50+
transaction = TransactionBuilder::template build<Transaction>(
5051
plan, input.to_address(), input.change_address(), TWCoinType(input.coin_type()));
5152
}
5253

5354
/// Initializes a transaction signer with signing input, a transaction, and
5455
/// a hash type.
55-
TransactionSigner(Bitcoin::Proto::SigningInput &&input, const TransactionPlan &plan)
56+
TransactionSigner(Bitcoin::Proto::SigningInput&& input, const TransactionPlan& plan)
5657
: input(input), plan(plan) {
57-
transaction = TransactionBuilder::build<Transaction>(
58+
transaction = TransactionBuilder::template build<Transaction>(
5859
plan, input.to_address(), input.change_address(), TWCoinType(input.coin_type()));
5960
}
6061

@@ -64,33 +65,33 @@ class TransactionSigner {
6465
Result<Transaction> sign();
6566

6667
private:
67-
Result<void> sign(Script script, size_t index, const Proto::UnspentTransaction &utxo);
68+
Result<void> sign(Script script, size_t index, const Proto::UnspentTransaction& utxo);
6869
Result<std::vector<Data>> signStep(Script script, size_t index,
69-
const Proto::UnspentTransaction &utxo, uint32_t version);
70-
Data createSignature(const Transaction &transaction, const Script &script, const Data &key,
70+
const Proto::UnspentTransaction& utxo, uint32_t version);
71+
Data createSignature(const Transaction& transaction, const Script& script, const Data& key,
7172
size_t index, Amount amount, uint32_t version);
72-
Data pushAll(const std::vector<Data> &results);
73+
Data pushAll(const std::vector<Data>& results);
7374

7475
/// Returns the private key for the given public key hash.
75-
Data keyForPublicKeyHash(const Data &hash) const;
76+
Data keyForPublicKeyHash(const Data& hash) const;
7677

7778
/// Returns the redeem script for the given script hash.
78-
Data scriptForScriptHash(const Data &hash) const;
79+
Data scriptForScriptHash(const Data& hash) const;
7980
};
8081

8182
} // namespace TW::Bitcoin
8283

8384
/// Wrapper for C interface.
8485
struct TWBitcoinTransactionSigner {
85-
TW::Bitcoin::TransactionSigner<TW::Bitcoin::Transaction> impl;
86+
TW::Bitcoin::TransactionSigner<TW::Bitcoin::Transaction, TW::Bitcoin::TransactionBuilder> impl;
8687
};
8788

8889
/// Wrapper for Zcash C interface.
8990
struct TWZcashTransactionSigner {
90-
TW::Bitcoin::TransactionSigner<TW::Zcash::Transaction> impl;
91+
TW::Bitcoin::TransactionSigner<TW::Zcash::Transaction, TW::Zcash::TransactionBuilder> impl;
9192
};
9293

9394
/// Wrapper for Groestlcoin C interface.
9495
struct TWGroestlcoinTransactionSigner {
95-
TW::Bitcoin::TransactionSigner<TW::Groestlcoin::Transaction> impl;
96+
TW::Bitcoin::TransactionSigner<TW::Groestlcoin::Transaction, TW::Bitcoin::TransactionBuilder> impl;
9697
};

src/Coin.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
#include "Harmony/Address.h"
2424
#include "Icon/Address.h"
2525
#include "IoTeX/Address.h"
26+
#include "Kusama/Address.h"
2627
#include "NEAR/Address.h"
28+
#include "NULS/Address.h"
2729
#include "Nano/Address.h"
2830
#include "Nebulas/Address.h"
2931
#include "Nimiq/Address.h"
30-
#include "NULS/Address.h"
3132
#include "Ontology/Address.h"
33+
#include "Polkadot/Address.h"
3234
#include "Ripple/Address.h"
3335
#include "Ripple/XAddress.h"
3436
#include "Solana/Address.h"
@@ -40,8 +42,6 @@
4042
#include "Waves/Address.h"
4143
#include "Zcash/TAddress.h"
4244
#include "Zilliqa/Address.h"
43-
#include "Kusama/Address.h"
44-
#include "Polkadot/Address.h"
4545

4646
#include <TrustWalletCore/TWHRP.h>
4747

@@ -50,7 +50,7 @@
5050

5151
using namespace TW;
5252

53-
bool TW::validateAddress(TWCoinType coin, const std::string &string) {
53+
bool TW::validateAddress(TWCoinType coin, const std::string& string) {
5454
auto p2pkh = TW::p2pkhPrefix(coin);
5555
auto p2sh = TW::p2shPrefix(coin);
5656
auto hrp = stringForHRP(TW::hrp(coin));
@@ -129,8 +129,7 @@ bool TW::validateAddress(TWCoinType coin, const std::string &string) {
129129
return Nimiq::Address::isValid(string);
130130

131131
case TWCoinTypeXRP:
132-
return Ripple::Address::isValid(string) ||
133-
Ripple::XAddress::isValid(string);
132+
return Ripple::Address::isValid(string) || Ripple::XAddress::isValid(string);
134133

135134
case TWCoinTypeStellar:
136135
case TWCoinTypeKin:
@@ -178,18 +177,18 @@ bool TW::validateAddress(TWCoinType coin, const std::string &string) {
178177

179178
case TWCoinTypeKusama:
180179
return Kusama::Address::isValid(string);
181-
180+
182181
case TWCoinTypePolkadot:
183182
return Polkadot::Address::isValid(string);
184183
}
185184
}
186185

187-
std::string TW::deriveAddress(TWCoinType coin, const PrivateKey &privateKey) {
186+
std::string TW::deriveAddress(TWCoinType coin, const PrivateKey& privateKey) {
188187
auto keyType = TW::publicKeyType(coin);
189188
return TW::deriveAddress(coin, privateKey.getPublicKey(keyType));
190189
}
191190

192-
std::string TW::deriveAddress(TWCoinType coin, const PublicKey &publicKey) {
191+
std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey) {
193192
auto p2pkh = TW::p2pkhPrefix(coin);
194193
auto hrp = stringForHRP(TW::hrp(coin));
195194

@@ -288,7 +287,7 @@ std::string TW::deriveAddress(TWCoinType coin, const PublicKey &publicKey) {
288287

289288
case TWCoinTypeNULS:
290289
return NULS::Address(publicKey).string();
291-
290+
292291
case TWCoinTypeNEAR:
293292
return NEAR::Address(publicKey).string();
294293

src/Zcash/Transaction.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "../BinaryCoding.h"
1010
#include "../Hash.h"
11+
#include "../HexCoding.h"
1112

1213
#include <cassert>
1314

@@ -22,8 +23,10 @@ const auto joinsplitsHashPersonalization = Data({'Z','c','a','s','h','J','S','p'
2223
const auto shieldedSpendHashPersonalization = Data({'Z','c','a','s','h','S','S','p','e','n','d','s','H','a','s','h'});
2324
const auto shieldedOutputsHashPersonalization = Data({'Z','c','a','s','h','S','O','u','t','p','u','t','H','a','s','h'});
2425

25-
/// See also https://github.com/zcash/zcash/blob/36243f41f1d8df98bdc834825ba539263f1da121/src/consensus/upgrades.cpp#L28
26-
constexpr std::array<byte, 4> saplingBranchID = {0xbb, 0x09, 0xb8, 0x76};
26+
/// See https://github.com/zcash/zips/blob/master/zip-0205.rst#sapling-deployment BRANCH_ID section
27+
const std::array<byte, 4> Zcash::SaplingBranchID = {0xbb, 0x09, 0xb8, 0x76};
28+
/// See https://github.com/zcash/zips/blob/master/zip-0206.rst#blossom-deployment BRANCH_ID section
29+
const std::array<byte, 4> Zcash::BlossomBranchID = {0x60, 0x0e, 0xb4, 0x2b};
2730

2831
Data Transaction::getPreImage(const Bitcoin::Script& scriptCode, size_t index, enum TWBitcoinSigHashType hashType,
2932
uint64_t amount) const {
@@ -95,7 +98,7 @@ Data Transaction::getPreImage(const Bitcoin::Script& scriptCode, size_t index, e
9598
// The input being signed (replacing the scriptSig with scriptCode + amount)
9699
// The prevout may already be contained in hashPrevout, and the nSequence
97100
// may already be contain in hashSequence.
98-
reinterpret_cast<const TW::Bitcoin::OutPoint&>(inputs[index].previousOutput).encode(data);
101+
reinterpret_cast<const Bitcoin::OutPoint&>(inputs[index].previousOutput).encode(data);
99102
scriptCode.encode(data);
100103

101104
encode64LE(amount, data);
@@ -182,10 +185,9 @@ Data Transaction::getSignatureHash(const Bitcoin::Script& scriptCode, size_t ind
182185
personalization.reserve(16);
183186
std::copy(sigHashPersonalization.begin(), sigHashPersonalization.begin() + 12,
184187
std::back_inserter(personalization));
185-
std::copy(saplingBranchID.begin(), saplingBranchID.end(), std::back_inserter(personalization));
186-
188+
std::copy(branchId.begin(), branchId.end(), std::back_inserter(personalization));
187189
auto preimage = getPreImage(scriptCode, index, hashType, amount);
188-
auto hash = TW::Hash::blake2b(preimage, 32, personalization);
190+
auto hash = Hash::blake2b(preimage, 32, personalization);
189191
return hash;
190192
}
191193

src/Zcash/Transaction.h

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@
1212
#include "../Bitcoin/TransactionOutput.h"
1313
#include "../proto/Bitcoin.pb.h"
1414

15+
#include <array>
1516
#include <vector>
1617

1718
namespace TW::Zcash {
1819

19-
/// Only supports Sapling transparent transaction right now
20+
extern const std::array<byte, 4> SaplingBranchID;
21+
extern const std::array<byte, 4> BlossomBranchID;
22+
23+
/// Only supports transparent transaction right now
2024
/// See also https://github.com/zcash/zips/blob/master/zip-0243.rst
2125
struct Transaction {
2226
uint32_t version = 0x80000004;
@@ -27,23 +31,25 @@ struct Transaction {
2731

2832
std::vector<Bitcoin::TransactionInput> inputs;
2933
std::vector<Bitcoin::TransactionOutput> outputs;
34+
std::array<byte, 4> branchId;
3035

3136
Transaction() = default;
3237

3338
Transaction(uint32_t version, uint32_t versionGroupId, uint32_t lockTime, uint32_t expiryHeight,
34-
uint64_t valueBalance)
39+
uint64_t valueBalance, std::array<byte, 4> branchId)
3540
: version(version)
3641
, versionGroupId(versionGroupId)
3742
, lockTime(lockTime)
3843
, expiryHeight(expiryHeight)
39-
, valueBalance(valueBalance) {}
44+
, valueBalance(valueBalance)
45+
, branchId(branchId) {}
4046

4147
/// Whether the transaction is empty.
4248
bool empty() const { return inputs.empty() && outputs.empty(); }
4349

4450
/// Generates the signature pre-image.
45-
Data getPreImage(const Bitcoin::Script& scriptCode, size_t index, enum TWBitcoinSigHashType hashType,
46-
uint64_t amount) const;
51+
Data getPreImage(const Bitcoin::Script& scriptCode, size_t index,
52+
enum TWBitcoinSigHashType hashType, uint64_t amount) const;
4753
Data getPrevoutHash() const;
4854
Data getSequenceHash() const;
4955
Data getOutputsHash() const;
@@ -55,8 +61,9 @@ struct Transaction {
5561
/// Encodes the rawtx into the provided buffer.
5662
void encode(Data& data) const;
5763

58-
Data getSignatureHash(const Bitcoin::Script& scriptCode, size_t index, enum TWBitcoinSigHashType hashType,
59-
uint64_t amount, TWBitcoinSignatureVersion version) const;
64+
Data getSignatureHash(const Bitcoin::Script& scriptCode, size_t index,
65+
enum TWBitcoinSigHashType hashType, uint64_t amount,
66+
TWBitcoinSignatureVersion version) const;
6067

6168
/// Converts to Protobuf model
6269
Bitcoin::Proto::Transaction proto() const;

0 commit comments

Comments
 (0)