Skip to content

Commit e089081

Browse files
author
AztecBot
committed
Merge branch 'next' into merge-train/barretenberg
2 parents 517f44f + d7934b9 commit e089081

File tree

18 files changed

+465
-26
lines changed

18 files changed

+465
-26
lines changed

docs/docs-developers/docs/resources/migration_notes.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,23 @@ FPCs that use only Fee Juice still work on all networks, since FeeJuice is a pro
9191

9292
Similarly, the `fpc-public` and `fpc-private` CLI wallet payment methods use the reference Token-based FPC and will not work on public networks. Use `fee_juice` for direct Fee Juice payment, or `fpc-sponsored` on devnet and local network.
9393

94+
### [aztec.js] `EmbeddedWalletOptions` now uses a unified `pxe` field
95+
96+
The `pxeConfig` and `pxeOptions` fields on `EmbeddedWalletOptions` have been deprecated in favor of a single `pxe` field that accepts both PXE configuration and dependency overrides (custom prover, store, simulator):
97+
98+
```diff
99+
const wallet = await EmbeddedWallet.create(nodeUrl, {
100+
- pxeConfig: { proverEnabled: true },
101+
- pxeOptions: { proverOrOptions: myCustomProver },
102+
+ pxe: {
103+
+ proverEnabled: true,
104+
+ proverOrOptions: myCustomProver,
105+
+ },
106+
});
107+
```
108+
109+
The old fields still work but will be removed in a future release.
110+
94111
### [Aztec.nr] Domain-separated tags on log emission
95112

96113
All logs emitted through the Aztec.nr framework now include a domain-separated tag at `fields[0]`. Each log category uses its own domain separator via `compute_log_tag(raw_tag, dom_sep)`:

noir-projects/noir-contracts/contracts/test/note_getter_contract/src/main.nr

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use aztec::macros::aztec;
2+
mod packed_note;
23

34
/// Used to test note getter in e2e_note_getter.test.ts
45
#[aztec]
@@ -16,9 +17,12 @@ pub contract NoteGetter {
1617

1718
use field_note::FieldNote;
1819

20+
use crate::packed_note::PackedNote;
21+
1922
#[storage]
2023
struct Storage<Context> {
2124
set: Owned<PrivateSet<FieldNote, Context>, Context>,
25+
packed_set: Owned<PrivateSet<PackedNote, Context>, Context>,
2226
}
2327

2428
#[external("private")]
@@ -42,4 +46,42 @@ pub contract NoteGetter {
4246
));
4347
notes.map(|note| note.value)
4448
}
49+
50+
#[external("private")]
51+
fn insert_packed_note(high: u8, low: u8) {
52+
let owner = self.msg_sender();
53+
let note = PackedNote { high, low };
54+
55+
self.storage.packed_set.at(owner).insert(note).deliver(MessageDelivery.ONCHAIN_CONSTRAINED);
56+
}
57+
58+
#[external("utility")]
59+
unconstrained fn select_packed_notes_by_high(
60+
owner: AztecAddress,
61+
comparator: u8,
62+
value: Field,
63+
) -> BoundedVec<[u8; 2], 10> {
64+
let selector = PackedNote::high_selector();
65+
let notes = self.storage.packed_set.at(owner).view_notes(NoteViewerOptions::new().select(
66+
selector,
67+
comparator,
68+
value,
69+
));
70+
notes.map(|note: PackedNote| [note.high, note.low])
71+
}
72+
73+
#[external("utility")]
74+
unconstrained fn select_packed_notes_by_low(
75+
owner: AztecAddress,
76+
comparator: u8,
77+
value: Field,
78+
) -> BoundedVec<[u8; 2], 10> {
79+
let selector = PackedNote::low_selector();
80+
let notes = self.storage.packed_set.at(owner).view_notes(NoteViewerOptions::new().select(
81+
selector,
82+
comparator,
83+
value,
84+
));
85+
notes.map(|note: PackedNote| [note.high, note.low])
86+
}
4587
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use aztec::{
2+
macros::notes::note,
3+
note::note_getter_options::PropertySelector,
4+
protocol::traits::{Deserialize, Packable, Serialize},
5+
};
6+
7+
/// A note that packs two u8 fields (high, low) into a single Field.
8+
/// The layout in the packed Field is: (high << 8) + low.
9+
/// This means in the big-endian 32-byte representation:
10+
/// - low occupies offset=0, length=1 (the least significant byte)
11+
/// - high occupies offset=1, length=1 (the second least significant byte)
12+
#[derive(Deserialize, Eq, Serialize)]
13+
#[note]
14+
pub struct PackedNote {
15+
pub high: u8,
16+
pub low: u8,
17+
}
18+
19+
impl Packable for PackedNote {
20+
let N: u32 = 1;
21+
22+
fn pack(self) -> [Field; Self::N] {
23+
[(self.high as Field) * 256 + (self.low as Field)]
24+
}
25+
26+
fn unpack(packed: [Field; Self::N]) -> Self {
27+
let low = packed[0] as u8;
28+
let high = ((packed[0] - low as Field) / 256) as u8;
29+
Self { high, low }
30+
}
31+
}
32+
33+
impl PackedNote {
34+
pub fn high_selector() -> PropertySelector {
35+
PropertySelector { index: 0, offset: 1, length: 1 }
36+
}
37+
38+
pub fn low_selector() -> PropertySelector {
39+
PropertySelector { index: 0, offset: 0, length: 1 }
40+
}
41+
}
42+
43+
mod test {
44+
use super::{Packable, PackedNote};
45+
46+
#[test]
47+
fn test_pack_unpack() {
48+
let note = PackedNote { high: 42, low: 7 };
49+
let unpacked = PackedNote::unpack(note.pack());
50+
assert_eq(unpacked.high, 42);
51+
assert_eq(unpacked.low, 7);
52+
}
53+
54+
#[test]
55+
fn test_pack_unpack_max() {
56+
let note = PackedNote { high: 255, low: 255 };
57+
let unpacked = PackedNote::unpack(note.pack());
58+
assert_eq(unpacked.high, 255);
59+
assert_eq(unpacked.low, 255);
60+
}
61+
}

yarn-project/aztec.js/src/contract/contract_function_interaction.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
130130
// docs:end:simulate
131131
if (this.functionDao.functionType == FunctionType.UTILITY) {
132132
const call = await this.getFunctionCall();
133+
const scopes = [...(options.additionalScopes ?? [])];
133134
const utilityResult = await this.wallet.executeUtility(call, {
134-
scopes: options.from === NO_FROM ? [] : [options.from],
135+
scopes: options.from === NO_FROM ? scopes : [options.from, ...scopes],
135136
authWitnesses: options.authWitnesses,
136137
});
137138

yarn-project/end-to-end/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ ultrahonk-bench-inputs
77
web/main.js*
88
consensys_web3signer_*
99
scripts/ha/postgres_data/
10+
.legacy-contracts/

yarn-project/end-to-end/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,30 @@ which will spawn the two processes.
2424
You can also run this by `docker-compose up` which will spawn 2 different containers for Anvil and the test runner.
2525

2626
You can run a single test by running `yarn test:compose <test_name>`.
27+
28+
## Running tests against legacy contract artifacts
29+
30+
To verify that contracts deployed from a previous release still work against the current stack, set
31+
`CONTRACT_ARTIFACTS_VERSION` to a published version of `@aztec/noir-contracts.js` / `@aztec/noir-test-contracts.js`:
32+
33+
```
34+
CONTRACT_ARTIFACTS_VERSION=4.1.3 yarn test:e2e src/e2e_amm.test.ts
35+
```
36+
37+
Only the JSON artifact files (`.../artifacts/*.json`) are redirected. The TypeScript wrapper classes
38+
(e.g. `TokenContract`) continue to load from the current workspace and use the current `@aztec/aztec.js` — so this
39+
exercises whether a deployed contract's ABI / bytecode / notes still work through the *new* client, not whether the
40+
legacy wrapper code still imports cleanly.
41+
42+
The first run downloads the pinned packages into `.legacy-contracts/<version>/node_modules/` (cached across runs). A
43+
startup banner and a per-redirect line are printed to stderr so you can confirm the legacy artifacts were actually
44+
loaded:
45+
46+
```
47+
[legacy-contracts][jest] CONTRACT_ARTIFACTS_VERSION=4.1.3
48+
[legacy-contracts][jest] redirecting @aztec/noir-contracts.js/artifacts/*.json -> .legacy-contracts/4.1.3/...
49+
[legacy-contracts][jest] redirected token_contract-Token.json -> /abs/.../.legacy-contracts/4.1.3/.../token_contract-Token.json
50+
```
51+
52+
When `CONTRACT_ARTIFACTS_VERSION` is unset the test run is byte-identical to the default behaviour. The cache is
53+
populated automatically on first use.

yarn-project/end-to-end/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
"moduleNameMapper": {
160160
"^(\\.{1,2}/.*)\\.[cm]?js$": "$1"
161161
},
162+
"resolver": "<rootDir>/legacy-jest-resolver.cjs",
162163
"testRegex": "./src/.*\\.test\\.(js|mjs|ts)$",
163164
"rootDir": "./src",
164165
"testTimeout": 120000,

yarn-project/end-to-end/src/e2e_note_getter.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,72 @@ describe('e2e_note_getter', () => {
7777
});
7878
});
7979

80+
describe('sub-field property selector', () => {
81+
let contract: NoteGetterContract;
82+
83+
beforeAll(async () => {
84+
({ contract } = await NoteGetterContract.deploy(wallet).send({ from: defaultAddress }));
85+
86+
// Insert packed notes with (high, low) pairs.
87+
// PackedNote packs two u8s into one Field: (high << 8) + low.
88+
// Sub-field selectors use Noir's LSB convention to extract individual u8 values.
89+
await Promise.all([
90+
contract.methods.insert_packed_note(1, 10).send({ from: defaultAddress }),
91+
contract.methods.insert_packed_note(2, 10).send({ from: defaultAddress }),
92+
contract.methods.insert_packed_note(1, 20).send({ from: defaultAddress }),
93+
contract.methods.insert_packed_note(3, 30).send({ from: defaultAddress }),
94+
]);
95+
});
96+
97+
it('filters by high sub-field', async () => {
98+
// high occupies offset=1, length=1 in the packed Field (second LSB)
99+
const { result } = await contract.methods
100+
.select_packed_notes_by_high(defaultAddress, Comparator.EQ, 1)
101+
.simulate({ from: defaultAddress });
102+
103+
const notes = boundedVecToArray(result) as bigint[][];
104+
expect(notes).toHaveLength(2);
105+
expect(notes.map(([h, l]) => [Number(h), Number(l)]).sort()).toEqual(
106+
[
107+
[1, 10],
108+
[1, 20],
109+
].sort(),
110+
);
111+
});
112+
113+
it('filters by low sub-field', async () => {
114+
// low occupies offset=0, length=1 in the packed Field (LSB)
115+
const { result } = await contract.methods
116+
.select_packed_notes_by_low(defaultAddress, Comparator.EQ, 10)
117+
.simulate({ from: defaultAddress });
118+
119+
const notes = boundedVecToArray(result) as bigint[][];
120+
expect(notes).toHaveLength(2);
121+
expect(notes.map(([h, l]) => [Number(h), Number(l)]).sort()).toEqual(
122+
[
123+
[1, 10],
124+
[2, 10],
125+
].sort(),
126+
);
127+
});
128+
129+
it('filters with GT comparator on sub-field', async () => {
130+
// low > 10 should match (1,20) and (3,30)
131+
const { result } = await contract.methods
132+
.select_packed_notes_by_low(defaultAddress, Comparator.GT, 10)
133+
.simulate({ from: defaultAddress });
134+
135+
const notes = boundedVecToArray(result) as bigint[][];
136+
expect(notes).toHaveLength(2);
137+
expect(notes.map(([h, l]) => [Number(h), Number(l)]).sort()).toEqual(
138+
[
139+
[1, 20],
140+
[3, 30],
141+
].sort(),
142+
);
143+
});
144+
});
145+
80146
describe('status filter', () => {
81147
let contract: TestContract;
82148
let owner: AztecAddress;

0 commit comments

Comments
 (0)