Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(michel-codec): add support for bytes #2310

Merged
merged 6 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions integration-tests/data/instructions-with-bytes-contracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import fs from 'fs';
import path from 'path';

export const addContract = fs.readFileSync(path.resolve(`${__dirname}/../../packages/taquito-michel-codec/test/contracts_016/opcodes/and_bytes.tz`)).toString();
zainen marked this conversation as resolved.
Show resolved Hide resolved
export const lslContract = fs.readFileSync(path.resolve(`${__dirname}/../../packages/taquito-michel-codec/test/contracts_016/opcodes/lsl_bytes.tz`)).toString();
export const lsrContract = fs.readFileSync(path.resolve(`${__dirname}/../../packages/taquito-michel-codec/test/contracts_016/opcodes/lsr_bytes.tz`)).toString();
export const notContract = fs.readFileSync(path.resolve(`${__dirname}/../../packages/taquito-michel-codec/test/contracts_016/opcodes/not_bytes.tz`)).toString();
export const orContract = fs.readFileSync(path.resolve(`${__dirname}/../../packages/taquito-michel-codec/test/contracts_016/opcodes/or_bytes.tz`)).toString();
export const xorContract = fs.readFileSync(path.resolve(`${__dirname}/../../packages/taquito-michel-codec/test/contracts_016/opcodes/xor_bytes.tz`)).toString();
90 changes: 90 additions & 0 deletions integration-tests/instructions-with-bytes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { CONFIGS } from "./config";
import { Protocols, TezosOperationError } from "@taquito/taquito";
import { addContract, lslContract, lsrContract, notContract, orContract, xorContract } from "./data/instructions-with-bytes-contracts";

CONFIGS().forEach(({ lib, protocol, setup }) => {
const Tezos = lib
const limanet = protocol === Protocols.PtLimaPtL ? test : test.skip;
const MumbaiAndMonday = protocol === Protocols.PtMumbaii || protocol === Protocols.ProtoALpha ? test: test.skip;
zainen marked this conversation as resolved.
Show resolved Hide resolved

describe(`Test origination of contract with instructions now supporting bytes`, () => {
zainen marked this conversation as resolved.
Show resolved Hide resolved

beforeEach(async (done) => {
await setup();
done();
});

MumbaiAndMonday(`Should be able to orignate contract with ADD parameter in michelson contract with bytes`, async done => {
const contract = await Tezos.contract.originate({
code: addContract,
storage: 0
});
await contract.confirmation();
expect(contract).toBeDefined();
zainen marked this conversation as resolved.
Show resolved Hide resolved
expect(contract.contractAddress).toContain("KT1");
done();
});
zainen marked this conversation as resolved.
Show resolved Hide resolved
MumbaiAndMonday(`Should be able to orignate contract with LSL parameter in michelson contract with bytes`, async done => {
const contract = await Tezos.contract.originate({
code: lslContract,
storage: 0
});
await contract.confirmation();
expect(contract).toBeDefined();
expect(contract.contractAddress).toContain("KT1");
done();
});
zainen marked this conversation as resolved.
Show resolved Hide resolved
MumbaiAndMonday(`Should be able to orignate contract with LSR parameter in michelson contract with bytes`, async done => {
const contract = await Tezos.contract.originate({
code: lsrContract,
storage: 0
});
await contract.confirmation();
expect(contract).toBeDefined();
expect(contract.contractAddress).toContain("KT1");
done();
});
MumbaiAndMonday(`Should be able to orignate contract with NOT parameter in michelson contract with bytes`, async done => {
const contract = await Tezos.contract.originate({
code: notContract,
storage: 0
});
await contract.confirmation();
expect(contract).toBeDefined();
expect(contract.contractAddress).toContain("KT1");
done();
});
MumbaiAndMonday(`Should be able to orignate contract with OR parameter in michelson contract with bytes`, async done => {
const contract = await Tezos.contract.originate({
code: orContract,
storage: 0
});
await contract.confirmation();
expect(contract).toBeDefined();
expect(contract.contractAddress).toContain("KT1");
done();
});
MumbaiAndMonday(`Should be able to orignate contract with XOR parameter in michelson contract with bytes`, async done => {
const contract = await Tezos.contract.originate({
code: xorContract,
storage: 0
});
await contract.confirmation();
expect(contract).toBeDefined();
expect(contract.contractAddress).toContain("KT1");
done();
});
limanet('Should fail to originate a contract for AND with bytes', async (done) => {
try {
const contract = await Tezos.contract.originate({
code: addContract,
storage: 0
});
await contract.confirmation();
} catch (err) {
expect(err).toBeInstanceOf(TezosOperationError);
}
done();
})
});
})
103 changes: 50 additions & 53 deletions packages/taquito-michel-codec/src/michelson-typecheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ function assertDataValidInternal(d: MichelsonData, t: MichelsonType, ctx: Contex
if (
'string' in d &&
checkDecodeTezosID(d.string, 'ED25519PublicKey', 'SECP256K1PublicKey', 'P256PublicKey') !==
null
null
zainen marked this conversation as resolved.
Show resolved Hide resolved
) {
return;
} else if ('bytes' in d) {
Expand Down Expand Up @@ -696,7 +696,7 @@ function functionTypeInternal(

// unpack instruction annotations and assert their maximum number
function instructionAnn(
num: { f?: number; t?: number; v?: number; },
num: { f?: number; t?: number; v?: number },
opt?: UnpackAnnotationsOptions
) {
const a = argAnn(instruction, {
Expand Down Expand Up @@ -727,10 +727,10 @@ function functionTypeInternal(
const ann =
a.v !== undefined || a.t !== undefined || a.f !== undefined
? [
...((a.v === null ? src.v : a.v) || []),
...((a.t === null ? src.t : a.t) || []),
...((a.f === null ? src.f : a.f) || []),
]
...((a.v === null ? src.v : a.v) || []),
...((a.t === null ? src.t : a.t) || []),
...((a.f === null ? src.f : a.f) || []),
]
: undefined;

const { annots: _annots, ...rest } = t;
Expand Down Expand Up @@ -778,12 +778,12 @@ function functionTypeInternal(
? ['@' + fieldAnn.slice(1)]
: undefined
: insVarAnn === '@%%'
? varAnn
? ['@' + varAnn.slice(1) + '.' + (fieldAnn ? fieldAnn.slice(1) : defField)]
: fieldAnn
? ['@' + fieldAnn.slice(1)]
: undefined
: [insVarAnn]
? varAnn
? ['@' + varAnn.slice(1) + '.' + (fieldAnn ? fieldAnn.slice(1) : defField)]
: fieldAnn
? ['@' + fieldAnn.slice(1)]
: undefined
: [insVarAnn]
: null,
});
}
Expand Down Expand Up @@ -1309,12 +1309,12 @@ function functionTypeInternal(

case 'LSL':
case 'LSR':
args(0, ['nat'], ['nat']);
args(0, ['nat', 'bytes'], ['nat', 'bytes']);
return [annotateVar({ prim: 'nat' }), ...stack.slice(2)];

case 'OR':
case 'XOR': {
const s = args(0, ['nat', 'bool'], ['nat', 'bool']);
const s = args(0, ['nat', 'bytes', 'bool'], ['nat', 'bytes', 'bool']);
if (s[0].prim !== s[1].prim) {
throw new MichelsonInstructionError(
instruction,
Expand All @@ -1326,7 +1326,7 @@ function functionTypeInternal(
}

case 'AND': {
const s = args(0, ['nat', 'bool', 'int'], ['nat', 'bool']);
const s = args(0, ['nat', 'bytes', 'bool', 'int'], ['nat', 'bytes', 'bool']);
if ((s[0].prim !== 'int' || s[1].prim !== 'nat') && s[0].prim !== s[1].prim) {
throw new MichelsonInstructionError(
instruction,
Expand All @@ -1338,7 +1338,7 @@ function functionTypeInternal(
}

case 'NOT': {
const s = args(0, ['nat', 'bool', 'int'])[0];
const s = args(0, ['nat', 'bytes', 'bool', 'int'])[0];
if (s.prim === 'bool') {
return [annotateVar({ prim: 'bool' }), ...stack.slice(1)];
}
Expand Down Expand Up @@ -1676,8 +1676,8 @@ function functionTypeInternal(
return s.prim === 'list'
? [annotateVar({ prim: 'list', args: [body[0]] }), ...tail]
: s.prim === 'map'
? [annotateVar({ prim: 'map', args: [s.args[0], body[0]] }), ...tail]
: [annotateVar({ prim: 'option', args: [body[0]] }), ...tail];
? [annotateVar({ prim: 'map', args: [s.args[0], body[0]] }), ...tail]
: [annotateVar({ prim: 'option', args: [body[0]] }), ...tail];
}

case 'ITER': {
Expand Down Expand Up @@ -1814,7 +1814,7 @@ function functionTypeInternal(
assertTypeAnnotationsValid(instruction.args[0]);
assertTypeAnnotationsValid(instruction.args[1]);
const s = [instruction.args[0]];
if (instruction.prim === "LAMBDA_REC") {
if (instruction.prim === 'LAMBDA_REC') {
s.push({ prim: 'lambda', args: [instruction.args[0], instruction.args[1]] });
}
const body = functionTypeInternal(instruction.args[2], s, {
Expand Down Expand Up @@ -1860,13 +1860,10 @@ function functionTypeInternal(
return [
annotateVar({
prim: 'option',
args: [
annotate({ prim: 'ticket', args: [s] }, instructionAnn({ t: 1, v: 1 }))
]
args: [annotate({ prim: 'ticket', args: [s] }, instructionAnn({ t: 1, v: 1 }))],
}),
...stack.slice(2)
...stack.slice(2),
];

}
}

Expand Down Expand Up @@ -1978,35 +1975,35 @@ function functionTypeInternal(
}
return ProtoInferiorTo(proto, Protocol.PtJakarta)
? [
annotateVar({
prim: 'option',
args: [
{
prim: 'pair',
args: [{ prim: 'int' }, annotate(s[1], { t: null })],
},
],
}),
...stack.slice(2),
]
annotateVar({
prim: 'option',
args: [
{
prim: 'pair',
args: [{ prim: 'int' }, annotate(s[1], { t: null })],
},
],
}),
...stack.slice(2),
]
: [
annotateVar({
prim: 'option',
args: [
{
prim: 'pair',
args: [
{ prim: 'bytes' },
{
prim: 'pair',
args: [{ prim: 'int' }, annotate(s[1], { t: null })],
},
],
},
],
}),
...stack.slice(2),
];
annotateVar({
prim: 'option',
args: [
{
prim: 'pair',
args: [
{ prim: 'bytes' },
{
prim: 'pair',
args: [{ prim: 'int' }, annotate(s[1], { t: null })],
},
],
},
],
}),
...stack.slice(2),
];
}

case 'OPEN_CHEST':
Expand Down Expand Up @@ -2070,7 +2067,7 @@ export function contractSection<T extends 'parameter' | 'storage' | 'code'>(
export function contractViews(contract: MichelsonContract): {
[name: string]: MichelsonContractView;
} {
const views: { [name: string]: MichelsonContractView; } = {};
const views: { [name: string]: MichelsonContractView } = {};
for (const s of contract) {
if (s.prim === 'view') {
views[s.args[0].string] = s;
Expand Down
56 changes: 56 additions & 0 deletions packages/taquito-michel-codec/test/contracts_016.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import fs from 'fs';
import path from 'path';
import { inspect } from 'util';
import { Contract, ContractOptions } from '../src/michelson-contract';
import { Protocol } from '../src/michelson-types';
import { MichelsonError } from '../src/utils';

const contracts: {
[group: string]: string[];
} = {
attic: [],
entrypoints: [],
ill_typed: [],
lib_protocol: [],
macros: [],
mini_scenarios: [],
non_regression: [],
opcodes: [
'and_bytes.tz',
'lsl_bytes.tz',
'lsr_bytes.tz',
'not_bytes.tz',
'or_bytes.tz',
'xor_bytes.tz',
],
};

describe('PtMumbaii', () => {
for (const [group, list] of Object.entries(contracts)) {
describe(group, () => {
for (const contract of list) {
it(contract, () => {
const options: ContractOptions = {
protocol: Protocol.PtLimaPtL,
};

const filename = path.resolve(__dirname, 'contracts_016', group, contract);
const src = fs.readFileSync(filename).toString();
if (group === 'ill_typed') {
expect(() => Contract.parse(src, options)).toThrow();
return;
}

try {
Contract.parse(src, options);
} catch (err) {
if (err instanceof MichelsonError) {
console.log(inspect(err, false, null));
}
throw err;
}
});
}
});
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
parameter unit;
storage unit;
code { DROP;

# 0x05 & 0x06 = 0x04
PUSH bytes 0x05; PUSH bytes 0x06; AND; PUSH bytes 0x04; ASSERT_CMPEQ;

# 0x0005 & 0x0106 = 0x0004, not 0x04
PUSH bytes 0x0005; PUSH bytes 0x0106; AND; PUSH bytes 0x0004; ASSERT_CMPEQ;

# Longer bytes is cut its prefix to have the same length as the shorter one
# 0x05 & 0x0106 = 0x05 & 0x06 = 0x04, not 0x0004
PUSH bytes 0x05; PUSH bytes 0x0106; AND; PUSH bytes 0x04; ASSERT_CMPEQ;

UNIT; NIL @noop operation; PAIR; };
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
parameter unit;
storage unit;
code { DROP;

# If shift is 0, LSL returns the bytes untouched
# 0x06 LSL 0 = 0x06
PUSH nat 0; PUSH bytes 0x06; LSL; PUSH bytes 0x06; ASSERT_CMPEQ;

# If shift is not 0, LSL returns a bytes longer than the original
# 0x06 LSL 1 = 0x000c (not 0x0c)
PUSH nat 1; PUSH bytes 0x06; LSL; PUSH bytes 0x000c; ASSERT_CMPEQ;

# 0x06 LSL 8 = 0x0600
PUSH nat 8; PUSH bytes 0x06; LSL; PUSH bytes 0x0600; ASSERT_CMPEQ;

# 0x0006 LSL 1 = 0x00000c (not 0x0c nor 0x000c)
PUSH nat 1; PUSH bytes 0x0006; LSL; PUSH bytes 0x00000c; ASSERT_CMPEQ;

UNIT; NIL @noop operation; PAIR; };
Loading