Skip to content

Commit

Permalink
Merge branch 'zsa-integration' into zsa-integration-issue-burn-valid
Browse files Browse the repository at this point in the history
  • Loading branch information
dmidem committed Jul 24, 2023
2 parents 12a306b + 7d2f057 commit 23817fb
Show file tree
Hide file tree
Showing 37 changed files with 1,165 additions and 305 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 77 additions & 0 deletions qa/rpc-tests/asset_issuance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
# Copyright (c) 2022 The Zcash developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://www.opensource.org/licenses/mit-license.php .

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
NU5_BRANCH_ID,
assert_equal,
nuparams,
start_nodes,
)
from test_framework.mininode import CTransaction
from io import BytesIO
from binascii import unhexlify

def issue_asset(self, node, address, asset_descr, amount, finalize):
txid = self.nodes[node].issue(0, address, asset_descr, amount, finalize)

tx = CTransaction()
f = BytesIO(unhexlify(self.nodes[node].getrawtransaction(txid).encode('ascii')))
tx.deserialize(f)

self.sync_all()
self.nodes[0].generate(1)
self.sync_all()

return tx.issueBundle.actions[0].notes[0].asset.hex()


def check_asset_balance(self, node, asset, expected_balance):
walletinfo = self.nodes[node].getwalletinfo()
assert_equal(walletinfo['asset_balances'][asset]['confirmed_balance'], expected_balance)


def orchard_address(node):
acct = node.z_getnewaccount()['account']
return node.z_getaddressforaccount(acct, ['orchard'])['address']


class IssueTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 4

def setup_nodes(self):
return start_nodes(self.num_nodes, self.options.tmpdir, [[
nuparams(NU5_BRANCH_ID, 205),
'-regtestwalletsetbestchaineveryblock'
]] * self.num_nodes)

def run_test(self):
# Sanity-check the test harness
assert_equal(self.nodes[0].getblockcount(), 200)

# Activate NU5
self.nodes[0].generate(5)
self.sync_all()

address0 = orchard_address(self.nodes[0])
address1 = orchard_address(self.nodes[1])

# Issue assets to an address on node 0
asset0 = issue_asset(self, 0, address0, "WBTC", 4000, False)
issue_asset(self, 0, address0, "WBTC", 2, False)
issue_asset(self, 0, address1, "WBTC", 23, False)

# Issue assets to an address on node 1
asset1 = issue_asset(self, 0, address1, "WETH", 42, False)

check_asset_balance(self, 0, asset0, 4002)
check_asset_balance(self, 1, asset0, 23)
check_asset_balance(self, 1, asset1, 42)

if __name__ == '__main__':
IssueTest().main()

44 changes: 20 additions & 24 deletions qa/rpc-tests/issuance.py → qa/rpc-tests/asset_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
assert_equal,
nuparams,
start_nodes,
wait_and_assert_operationid_status
)
from asset_issuance import issue_asset, check_asset_balance, orchard_address


class IssueTest(BitcoinTestFramework):
def __init__(self):
Expand All @@ -26,41 +29,34 @@ def run_test(self):
# Sanity-check the test harness
assert_equal(self.nodes[0].getblockcount(), 200)

# Get a new Orchard account on node 0
acct0 = self.nodes[0].z_getnewaccount()['account']
ua0 = self.nodes[0].z_getaddressforaccount(acct0, ['orchard'])['address']

# Get a new Orchard account on node 1
acct1 = self.nodes[1].z_getnewaccount()['account']
ua1 = self.nodes[1].z_getaddressforaccount(acct1, ['orchard'])['address']

# Activate NU5
self.nodes[0].generate(5)
self.sync_all()

address0 = orchard_address(self.nodes[0])
address1 = orchard_address(self.nodes[1])
address2 = orchard_address(self.nodes[2])

issued = 4003
transfer1 = 1
transfer2 = 2

# Issue assets to an address on node 0
self.nodes[0].issue(0, ua0, "WBTC", 4001, True)
asset = issue_asset(self, 0, address0, "WBTC", issued, True)
check_asset_balance(self, 0, asset, issued)

# Issue assets to an address on node 1
self.nodes[0].issue(0, ua1, "WBTC", 42, True)
# Send assets from node 0 to node 1
recipients = [{"address": address1, "amount": transfer1, "asset": asset}, {"address": address2, "amount": transfer2, "asset": asset}]
opid = self.nodes[0].z_sendassets(address0, recipients, 1)
wait_and_assert_operationid_status(self.nodes[0], opid)

self.sync_all()
self.nodes[0].generate(1)
self.sync_all()

walletinfo0 = self.nodes[0].getwalletinfo()
print(walletinfo0)
assert_equal(len(walletinfo0['asset_balances'].items()), 1)
for key, value in walletinfo0['asset_balances'].items():
assert_equal(value['confirmed_balance'], 4001)


walletinfo1 = self.nodes[1].getwalletinfo()
print(walletinfo1)
assert_equal(len(walletinfo1['asset_balances'].items()), 1)
for key, value in walletinfo1['asset_balances'].items():
assert_equal(value['confirmed_balance'], 42)

check_asset_balance(self, 0, asset, issued - transfer1 - transfer2)
check_asset_balance(self, 1, asset, transfer1)
check_asset_balance(self, 2, asset, transfer2)


if __name__ == '__main__':
Expand Down
69 changes: 64 additions & 5 deletions qa/rpc-tests/test_framework/mininode.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,20 +524,79 @@ def __repr__(self):
)


class Note(object):
def __init__(self):
self.recipient = None
self.value = 0
self.asset = None
self.rho = None
self.rseed = None

def deserialize(self, f):
self.recipient = f.read(43)
self.value = struct.unpack("<q", f.read(8))[0]
self.asset = f.read(32)
self.rho = f.read(32)
self.rseed = f.read(32)

def serialize(self):
r = b""
r += self.recipient
r += struct.pack("<q", self.value)
r += self.asset
r += self.rho
r += self.rseed
return r

def __repr__(self):
return "Note(value=%i, asset=%s)" % (
self.value,
self.asset.hex(),
)

class IssueAction(object):
def __init__(self):
self.asset_desc = None
self.finalize = False
self.notes = []

def deserialize(self, f):
self.finalize = f.read(1)[0] != 0
self.notes = deser_vector(f, Note)
self.asset_desc = deser_char_vector(f)

def serialize(self):
r = b""
r += struct.pack("B", 1 if self.finalize else 0)
r += ser_vector(self.notes)
r += ser_char_vector(self.asset_desc)
return r

def __repr__(self):
return "IssueAction(notes=%r)" % self.notes

class IssueBundle(object):
def __init__(self):
self.actions = []
self.ik = None
self.authorization = None

def deserialize(self, f):
num_actions = struct.unpack("<B", f.read(1))[0]
if num_actions != 0:
raise Exception("Non-empty IssueBundle is not implemented in mininode")
self.actions = deser_vector(f, IssueAction)
if len(self.actions) > 0:
self.ik = f.read(32)
self.authorization = f.read(64)

def serialize(self):
r = b""
r += ser_compact_size(0)
r += ser_vector(self.actions)
if len(self.actions) > 0:
r += self.ik
r += self.authorization
return r

def __repr__(self):
return "IssueBundle(Empty)"
return "IssueBundle(actions=%r)" % self.actions

class Groth16Proof(object):
def __init__(self):
Expand Down
10 changes: 10 additions & 0 deletions qa/zcash/smoke_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,16 @@ def z_sendmany(results, case, zcash, from_addr, recipients, privacy_policy):
def issue(results, case, zcash, account, addr, asset, amount, finalize):
return async_txid_cmd(results, case, zcash, 'issue', [account, addr, asset, amount, finalize])

def z_sendassets(results, case, zcash, from_addr, recipients):
return async_txid_cmd(results, case, zcash, 'z_sendassets', [
from_addr,
[{
'address': to_addr,
'amount': amount,
'asset': asset,
} for (to_addr, amount, asset) in recipients]
])

def check_z_sendmany(results, case, zcash, from_addr, recipients, privacy_policy):
txid = z_sendmany(results, case, zcash, from_addr, recipients, privacy_policy)
if txid is None:
Expand Down
10 changes: 10 additions & 0 deletions src/Asset.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ class Asset {
std::copy(id, id + ZC_ORCHARD_ZSA_ASSET_ID_SIZE, this->id);
}

Asset(std::string hex) {
std::vector<unsigned char> bytes;
for (unsigned int i = 0; i < hex.length(); i += 2) {
std::string byteString = hex.substr(i, 2);
unsigned char byte = (unsigned char) strtol(byteString.c_str(), NULL, 16);
bytes.push_back(byte);
}
std::copy(bytes.begin(), bytes.end(), this->id);
}

/**
* Similar to 'derive' method from Rust
* @param ik asset issuance key
Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ BITCOIN_CORE_H = \
wallet/asyncrpcoperation_mergetoaddress.h \
wallet/asyncrpcoperation_saplingmigration.h \
wallet/asyncrpcoperation_sendmany.h \
wallet/asyncrpcoperation_sendassets.h \
wallet/asyncrpcoperation_shieldcoinbase.h \
wallet/wallet_tx_builder.h \
wallet/crypter.h \
Expand Down Expand Up @@ -411,6 +412,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/asyncrpcoperation_mergetoaddress.cpp \
wallet/asyncrpcoperation_saplingmigration.cpp \
wallet/asyncrpcoperation_sendmany.cpp \
wallet/asyncrpcoperation_sendassets.cpp \
wallet/asyncrpcoperation_shieldcoinbase.cpp \
wallet/wallet_tx_builder.cpp \
wallet/crypter.cpp \
Expand Down
8 changes: 4 additions & 4 deletions src/gtest/test_checktransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1415,14 +1415,14 @@ TEST(ChecktransactionTests, NU5AcceptsOrchardShieldedCoinbase) {
.ToIncomingViewingKey()
.Address(0);
uint256 ovk;
builder.AddOutput(ovk, to, CAmount(123456), std::nullopt);
builder.AddOutput(ovk, to, CAmount(123456), std::nullopt, Asset::Native());

// orchard::Builder pads to two Actions, but does so using a "no OVK" policy for
// dummy outputs, which violates coinbase rules requiring all shielded outputs to
// be recoverable. We manually add a dummy output to sidestep this issue.
// TODO: If/when we have funding streams going to Orchard recipients, this dummy
// output can be removed.
builder.AddOutput(ovk, to, 0, std::nullopt);
builder.AddOutput(ovk, to, 0, std::nullopt, Asset::Native());

auto bundle = builder
.Build().value()
Expand Down Expand Up @@ -1537,14 +1537,14 @@ TEST(ChecktransactionTests, NU5EnforcesOrchardRulesOnShieldedCoinbase) {
.ToIncomingViewingKey()
.Address(0);
uint256 ovk;
builder.AddOutput(ovk, to, CAmount(1000), std::nullopt);
builder.AddOutput(ovk, to, CAmount(1000), std::nullopt, Asset::Native());

// orchard::Builder pads to two Actions, but does so using a "no OVK" policy for
// dummy outputs, which violates coinbase rules requiring all shielded outputs to
// be recoverable. We manually add a dummy output to sidestep this issue.
// TODO: If/when we have funding streams going to Orchard recipients, this dummy
// output can be removed.
builder.AddOutput(ovk, to, 0, std::nullopt);
builder.AddOutput(ovk, to, 0, std::nullopt, Asset::Native());

auto bundle = builder
.Build().value()
Expand Down
2 changes: 1 addition & 1 deletion src/gtest/test_mempoollimit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ TEST(MempoolLimitTests, MempoolCostAndEvictionWeight)
auto builder = TransactionBuilder(Params(), 1, std::nullopt);
builder.AddSaplingSpend(sk, testNote.note, testNote.tree.witness());
builder.AddSaplingOutput(fvk.ovk, pa, 25000, {});
static_assert(MINIMUM_FEE == 10000);
static_assert(MINIMUM_FEE == 0); // TODO re-enable fees
builder.SetFee(MINIMUM_FEE-1);

auto [cost, evictionWeight] = MempoolCostAndEvictionWeight(builder.Build().GetTxOrThrow(), MINIMUM_FEE-1);
Expand Down
2 changes: 1 addition & 1 deletion src/gtest/test_transaction_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ TEST(TransactionBuilder, TransparentToOrchard)
// 0.00005 t-ZEC in, 0.00004 z-ZEC out, default fee
auto builder = TransactionBuilder(Params(), 1, orchardAnchor, &keystore);
builder.AddTransparentInput(COutPoint(uint256S("1234"), 0), scriptPubKey, 5000);
builder.AddOrchardOutput(std::nullopt, recipient, 4000, std::nullopt);
builder.AddOrchardOutput(std::nullopt, recipient, 4000, std::nullopt, Asset::Native());
auto maybeTx = builder.Build();
EXPECT_TRUE(maybeTx.IsTx());
if (maybeTx.IsError()) {
Expand Down
Loading

0 comments on commit 23817fb

Please sign in to comment.