Skip to content

Commit

Permalink
Add support for compilation from string
Browse files Browse the repository at this point in the history
- Rename fromCashFile to compile
  - Supports both file and string
  - Old name can still be used but is deprecated
- Rename fromArtifact to import
  - Supports both file and JSON object
  - Old name can still be used but is deprecated
- Export now returns the artifact and does not require fn argument
- Update examples
  • Loading branch information
rkalis committed Nov 8, 2019
1 parent b05846c commit 78b93c4
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 53 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Using the CashScript SDK, you can import / compile existing cash contract files,
```ts
...
// Compile the P2PKH Cash Contract
const P2PKH: Contract = Contract.fromCashFile(path.join(__dirname, 'p2pkh.cash'), 'testnet');
const P2PKH: Contract = Contract.compile(path.join(__dirname, 'p2pkh.cash'), 'testnet');

// Instantiate a new P2PKH contract with constructor arguments: { pkh: pkh }
const instance: Instance = P2PKH.new(pkh);
Expand Down
5 changes: 1 addition & 4 deletions examples/Escrow/Escrow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ export async function run(): Promise<void> {
const oracle: Oracle = new Oracle(bitbox.HDNode.toKeyPair(alice));

// Compile and instantiate Escrow contract
const Escrow: Contract = Contract.fromCashFile(
path.join(__dirname, 'Escrow.cash'),
network,
);
const Escrow: Contract = Contract.compile(path.join(__dirname, 'Escrow.cash'), network);
const escrowKey: Buffer = Buffer.from('01', 'hex');
const actionByte: Buffer = Buffer.from('01', 'hex');
const instance: Instance = Escrow.new(
Expand Down
2 changes: 1 addition & 1 deletion examples/hodl_vault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export async function run(): Promise<void> {
);

// Compile and instantiate HODL Vault
const HodlVault: Contract = Contract.fromCashFile(path.join(__dirname, 'hodl_vault.cash'), 'testnet');
const HodlVault: Contract = Contract.compile(path.join(__dirname, 'hodl_vault.cash'), 'testnet');
const instance: Instance = HodlVault.new(
bitbox.ECPair.toPublicKey(owner),
bitbox.ECPair.toPublicKey(oracle.keypair),
Expand Down
2 changes: 1 addition & 1 deletion examples/meep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function run(): Promise<void> {
const alicePkh: Buffer = bitbox.Crypto.hash160(alicePk);

// Compile the P2PKH Cash Contract
const P2PKH: Contract = Contract.fromCashFile(path.join(__dirname, 'p2pkh.cash'), network);
const P2PKH: Contract = Contract.compile(path.join(__dirname, 'p2pkh.cash'), network);

// Instantiate a new P2PKH contract with constructor arguments: { pkh: alicePkh }
const instance: Instance = P2PKH.new(alicePkh);
Expand Down
2 changes: 1 addition & 1 deletion examples/memo_cash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function run(): Promise<void> {
const alicePkh: Buffer = bitbox.Crypto.hash160(alicePk);

// Compile the P2PKH Cash Contract
const P2PKH: Contract = Contract.fromCashFile(path.join(__dirname, 'p2pkh.cash'), network);
const P2PKH: Contract = Contract.compile(path.join(__dirname, 'p2pkh.cash'), network);

// Instantiate a new P2PKH contract with constructor arguments: { pkh: alicePkh }
const instance: Instance = P2PKH.new(alicePkh);
Expand Down
2 changes: 1 addition & 1 deletion examples/p2pkh-artifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function run(): Promise<void> {
const alicePkh: Buffer = bitbox.Crypto.hash160(alicePk);

// Import the P2PKH Cash Contract from Artifact file, including deployed contract details
const P2PKH: Contract = Contract.fromArtifact(path.join(__dirname, 'p2pkh.json'), network);
const P2PKH: Contract = Contract.import(path.join(__dirname, 'p2pkh.json'), network);

// Instantiate a new P2PKH contract with constructor arguments: { pkh: alicePkh }
let instance: Instance = P2PKH.new(alicePkh);
Expand Down
2 changes: 1 addition & 1 deletion examples/p2pkh.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async function run() {
const alicePkh = bitbox.Crypto.hash160(alicePk);

// Compile the P2PKH Cash Contract
const P2PKH = Contract.fromCashFile(path.join(__dirname, 'p2pkh.cash'), network);
const P2PKH = Contract.compile(path.join(__dirname, 'p2pkh.cash'), network);

// Instantiate a new P2PKH contract with constructor arguments: { pkh: alicePkh }
const instance = P2PKH.new(alicePkh);
Expand Down
2 changes: 1 addition & 1 deletion examples/p2pkh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function run(): Promise<void> {
const alicePkh: Buffer = bitbox.Crypto.hash160(alicePk);

// Compile the P2PKH Cash Contract
const P2PKH: Contract = Contract.fromCashFile(path.join(__dirname, 'p2pkh.cash'), network);
const P2PKH: Contract = Contract.compile(path.join(__dirname, 'p2pkh.cash'), network);

// Instantiate a new P2PKH contract with constructor arguments: { pkh: alicePkh }
const instance: Instance = P2PKH.new(alicePkh);
Expand Down
2 changes: 1 addition & 1 deletion examples/slp_genesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function run(): Promise<void> {
const alicePkh: Buffer = bitbox.Crypto.hash160(alicePk);

// Compile the P2PKH Cash Contract
const P2PKH: Contract = Contract.fromCashFile(path.join(__dirname, 'p2pkh.cash'), network);
const P2PKH: Contract = Contract.compile(path.join(__dirname, 'p2pkh.cash'), network);

// Instantiate a new P2PKH contract with constructor arguments: { pkh: alicePkh }
const instance: Instance = P2PKH.new(alicePkh);
Expand Down
2 changes: 1 addition & 1 deletion examples/transfer_with_timeout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export async function run(): Promise<void> {
const bobPk: Buffer = bitbox.ECPair.toPublicKey(bob);

// Compile the TransferWithTimeout Cash Contract
const TransferWithTimeout: Contract = Contract.fromCashFile(
const TransferWithTimeout: Contract = Contract.compile(
path.join(__dirname, 'transfer_with_timeout.cash'), network,
);

Expand Down
2 changes: 1 addition & 1 deletion packages/cashscript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Using the CashScript SDK, you can import / compile existing cash contract files,
```ts
...
// Compile the P2PKH Cash Contract
const P2PKH: Contract = Contract.fromCashFile(path.join(__dirname, 'p2pkh.cash'), 'testnet');
const P2PKH: Contract = Contract.compile(path.join(__dirname, 'p2pkh.cash'), 'testnet');

// Instantiate a new P2PKH contract with constructor arguments: { pkh: pkh }
const instance: Instance = P2PKH.new(pkh);
Expand Down
30 changes: 24 additions & 6 deletions packages/cashscript/src/Contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CashCompiler,
Data,
} from 'cashc';
import * as fs from 'fs';
import { Transaction } from './Transaction';
import { Instance } from './Instance';
import { Parameter, encodeParameter, Sig } from './Parameter';
Expand All @@ -13,18 +14,35 @@ export class Contract {
new: (...params: Parameter[]) => Instance;
deployed: (at?: string) => Instance;

static fromCashFile(fn: string, network?: string): Contract {
const artifact = CashCompiler.compileFile(fn);
static compile(fnOrString: string, network?: string): Contract {
const artifact = fs.existsSync(fnOrString)
? CashCompiler.compileFile(fnOrString)
: CashCompiler.compileString(fnOrString);

return new Contract(artifact, network);
}

static fromArtifact(fn: string, network?: string): Contract {
const artifact = Artifacts.require(fn);
static import(fnOrArtifact: string | Artifact, network?: string): Contract {
const artifact = typeof fnOrArtifact === 'string'
? Artifacts.require(fnOrArtifact)
: fnOrArtifact;

return new Contract(artifact, network);
}

export(fn: string): void {
Artifacts.export(this.artifact, fn);
// @deprecated
static fromCashFile(fnOrString: string, network?: string): Contract {
return this.compile(fnOrString, network);
}

// @deprecated
static fromArtifact(fnOrArtifact: string | Artifact, network?: string): Contract {
return this.import(fnOrArtifact, network);
}

export(fn?: string): Artifact {
if (typeof fn !== 'undefined') Artifacts.export(this.artifact, fn);
return this.artifact;
}

constructor(
Expand Down
118 changes: 93 additions & 25 deletions packages/cashscript/test/Contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,70 @@ import * as path from 'path';
import { Contract, Artifacts } from '../src';

describe('Contract', () => {
describe('fromCashFile', () => {
it('should fail for invalid .cash file', () => {
describe('compile', () => {
it('should fail for invalid .cash file or string', () => {
assert.throws(() => {
Contract.fromCashFile(path.join(__dirname, 'fixture', 'p2pkh-invalid.cash'));
Contract.compile(path.join(__dirname, 'fixture', 'p2pkh-invalid.cash'));
});

assert.throws(() => {
Contract.compile('contract P2PKH(bytes20 pkh) {\n // Require pk to match stored pkh and signature to match\n functon spend(pubkey pk, sig s) {\n require(hash160(pk) == pkh);\n require(checkSig(s, pk));\n }\n}\n');
});
});

it('should create P2PKH Contract object from file', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'p2pkh.json'));
const P2PKH = Contract.compile(path.join(__dirname, 'fixture', 'p2pkh.cash'));

// Disregard updatedAt & networks
expectedArtifact.updatedAt = P2PKH.artifact.updatedAt;
expectedArtifact.networks = P2PKH.artifact.networks;
assert.deepEqual(P2PKH.artifact, expectedArtifact);
});

it('should create P2PKH Contract object', () => {
const P2PKH = Contract.fromCashFile(path.join(__dirname, 'fixture', 'p2pkh.cash'));
it('should create P2PKH Contract object from string', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'p2pkh.json'));
const P2PKH = Contract.compile(expectedArtifact.source);

// Disregard updatedAt & networks
expectedArtifact.updatedAt = P2PKH.artifact.updatedAt;
expectedArtifact.networks = P2PKH.artifact.networks;
assert.deepEqual(P2PKH.artifact, expectedArtifact);
});

it('should create TransferWithTimeout Contract object', () => {
const TransferWithTimeout = Contract.fromCashFile(path.join(__dirname, 'fixture', 'transfer_with_timeout.cash'));
it('should create TransferWithTimeout Contract object from file', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'transfer_with_timeout.json'));
const TransferWithTimeout = Contract.compile(path.join(__dirname, 'fixture', 'transfer_with_timeout.cash'));

// Disregard updatedAt & networks
expectedArtifact.updatedAt = TransferWithTimeout.artifact.updatedAt;
expectedArtifact.networks = TransferWithTimeout.artifact.networks;
assert.deepEqual(TransferWithTimeout.artifact, expectedArtifact);
});

it('should create TransferWithTimeout Contract object from string', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'transfer_with_timeout.json'));
const TransferWithTimeout = Contract.compile(expectedArtifact.source);

// Disregard updatedAt & networks
expectedArtifact.updatedAt = TransferWithTimeout.artifact.updatedAt;
expectedArtifact.networks = TransferWithTimeout.artifact.networks;
assert.deepEqual(TransferWithTimeout.artifact, expectedArtifact);
});

it('should create HodlVault Contract object from file', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'hodl_vault.json'));
const HodlVault = Contract.compile(path.join(__dirname, 'fixture', 'hodl_vault.cash'));

// Disregard updatedAt & networks
expectedArtifact.updatedAt = HodlVault.artifact.updatedAt;
expectedArtifact.networks = HodlVault.artifact.networks;
assert.deepEqual(HodlVault.artifact, expectedArtifact);
});

it('should create HodlVault Contract object', () => {
const HodlVault = Contract.fromCashFile(path.join(__dirname, 'fixture', 'hodl_vault.cash'));
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'hodl_vault.json'));
const HodlVault = Contract.compile(expectedArtifact.source);

// Disregard updatedAt & networks
expectedArtifact.updatedAt = HodlVault.artifact.updatedAt;
Expand All @@ -41,48 +75,82 @@ describe('Contract', () => {
});
});

describe('fromArtifact', () => {
it('should fail with invalid Artifact file', () => {
describe('import', () => {
it('should fail with invalid Artifact file or object', () => {
assert.throws(() => {
Contract.fromArtifact(path.join(__dirname, 'fixture', 'p2pkh-invalid.json'), 'testnet');
Contract.import(path.join(__dirname, 'fixture', 'p2pkh-invalid.json'), 'testnet');
});
assert.throws(() => {
const artifact = Artifacts.require(path.join(__dirname, 'fixture', 'p2pkh-invalid.json'));
artifact.abi = [];
Contract.import(artifact);
});
});

it('should create P2PKH Contract object from file', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'p2pkh.json'));
const P2PKH = Contract.import(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');

assert.deepEqual(P2PKH.artifact, expectedArtifact);
});

it('should create P2PKH Contract object', () => {
const P2PKH = Contract.fromArtifact(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');
it('should create P2PKH Contract object from object', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'p2pkh.json'));
const P2PKH = Contract.import(expectedArtifact, 'testnet');

assert.deepEqual(P2PKH.artifact, expectedArtifact);
});

it('should create TransferWithTimeout Contract object', () => {
const TransferWithTimeout = Contract.fromArtifact(path.join(__dirname, 'fixture', 'transfer_with_timeout.json'), 'testnet');
it('should create TransferWithTimeout Contract object from file', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'transfer_with_timeout.json'));
const TransferWithTimeout = Contract.import(path.join(__dirname, 'fixture', 'transfer_with_timeout.json'), 'testnet');

assert.deepEqual(TransferWithTimeout.artifact, expectedArtifact);
});

it('should create HodlVault Contract object', () => {
const HodlVault = Contract.fromArtifact(path.join(__dirname, 'fixture', 'hodl_vault.json'), 'testnet');
it('should create TransferWithTimeout Contract object from object', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'transfer_with_timeout.json'));
const TransferWithTimeout = Contract.import(expectedArtifact, 'testnet');

assert.deepEqual(TransferWithTimeout.artifact, expectedArtifact);
});

it('should create HodlVault Contract object from file', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'hodl_vault.json'));
const HodlVault = Contract.import(path.join(__dirname, 'fixture', 'hodl_vault.json'), 'testnet');

assert.deepEqual(HodlVault.artifact, expectedArtifact);
});

it('should create HodlVault Contract object from object', () => {
const expectedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'hodl_vault.json'));
const HodlVault = Contract.import(expectedArtifact, 'testnet');

assert.deepEqual(HodlVault.artifact, expectedArtifact);
});
});

describe('export', () => {
it('should export Artifact file', () => {
const initial = Contract.fromArtifact(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');
initial.export(path.join(__dirname, 'fixture', 'p2pkh.json'));
const initial = Contract.import(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');
const returnedArtifact = initial.export(path.join(__dirname, 'fixture', 'p2pkh.json'));
const exportedArtifact = Artifacts.require(path.join(__dirname, 'fixture', 'p2pkh.json'));

assert.deepEqual(initial.artifact, exportedArtifact);
assert.deepEqual(initial.artifact, returnedArtifact);
});

it('should export Artifact object', () => {
const initial = Contract.import(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');
const artifact = initial.export();

assert.deepEqual(initial.artifact, artifact);
});
});

describe('new', () => {
it('should fail with incorrect constructor parameters', () => {
const P2PKH = Contract.fromArtifact(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');
const P2PKH = Contract.import(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');

assert.throws(() => {
P2PKH.new();
Expand All @@ -102,7 +170,7 @@ describe('Contract', () => {
});

it('should create new P2PKH instance', () => {
const P2PKH = Contract.fromArtifact(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');
const P2PKH = Contract.import(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');

const instance = P2PKH.new(Buffer.alloc(20, 0));

Expand All @@ -112,7 +180,7 @@ describe('Contract', () => {
});

it('should create new TransferWithTimeout instance', () => {
const TransferWithTimeout = Contract.fromArtifact(path.join(__dirname, 'fixture', 'transfer_with_timeout.json'), 'testnet');
const TransferWithTimeout = Contract.import(path.join(__dirname, 'fixture', 'transfer_with_timeout.json'), 'testnet');

const instance = TransferWithTimeout.new(Buffer.alloc(65, 0), Buffer.alloc(65, 0), 1000000);

Expand All @@ -123,7 +191,7 @@ describe('Contract', () => {
});

it('should create new HodlVault instance', () => {
const HodlVault = Contract.fromArtifact(path.join(__dirname, 'fixture', 'hodl_vault.json'), 'testnet');
const HodlVault = Contract.import(path.join(__dirname, 'fixture', 'hodl_vault.json'), 'testnet');

const instance = HodlVault.new(Buffer.alloc(65, 0), Buffer.alloc(65, 0), 1000000, 10000);

Expand All @@ -135,7 +203,7 @@ describe('Contract', () => {

describe('deployed', () => {
it('should fail on new Contract', () => {
const P2PKH = Contract.fromCashFile(path.join(__dirname, 'fixture', 'p2pkh.cash'), 'testnet');
const P2PKH = Contract.compile(path.join(__dirname, 'fixture', 'p2pkh.cash'), 'testnet');

assert.isEmpty(P2PKH.artifact.networks);
assert.throws(() => {
Expand All @@ -147,7 +215,7 @@ describe('Contract', () => {
});

it('should return deployed P2PKH', () => {
const P2PKH = Contract.fromArtifact(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');
const P2PKH = Contract.import(path.join(__dirname, 'fixture', 'p2pkh.json'), 'testnet');
const deployedAddress = 'bchtest:pzfsp649y00eay9mm3ky63ln72v3h6tx6gul8mlg93';

assert.isNotEmpty(P2PKH.artifact.networks);
Expand Down
Loading

0 comments on commit 78b93c4

Please sign in to comment.