Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
thekevinbrown committed Apr 29, 2019
2 parents 08b7283 + 99aad26 commit 2f21013
Show file tree
Hide file tree
Showing 16 changed files with 426 additions and 179 deletions.
60 changes: 42 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ From there you just need to add node scripts to your `package.json` file that tr

```
{
...
"scripts": {
"build": "lamington build",
"start": "lamington start eos",
"stop": "lamington stop eos",
"test": "lamington test"
},
...
...
"scripts": {
"build": "lamington build",
"start": "lamington start eos",
"stop": "lamington stop eos",
"test": "lamington test"
},
...
}
```

Expand Down Expand Up @@ -80,42 +80,64 @@ Lamington automatically searches for all files with the `.cpp` file extension be
Not every `.cpp` file is a build ready contract. So we added an additional ignore file, rightly named `.lamingtonignore`, to configure directories, files and patterns you don't want added to your build process. The `.lamingtonignore` follows the same syntax as a standard `.gitignore`, requiring a line separated list of ignore definitions. We've added the command line method `lamington ignore` to generate a `.lamingtonignore` file with default settings.

#### Specifying Build Contracts

If you'd like to run builds on specific contracts, an additional contract `identifier` can be specified like so;

```
lamington build [identifier]
$ lamington build [identifier]
```

_Replace the `[identifier]` with the relative path to the contract with or without the .cpp extension._

### Testing

Lamington was built with testing in mind. We considered the most commonly used testing libraries like Mocha when developing the Lamington toolset. Running your test suit is as easy as;

```
lamington test
$ lamington test
```

This command uses Mocha to run all your tests.
For a full list of available JavaScript utilities, please [visit the documentation here](https://docs.lamington.io/testing).

## Configuration
### Initialization

Lamington ships with a default configuration to make getting started simple and setup free. However, as your project grows, so will your need for additional Lamington configuration. For example, deployment to a testnet or the live network will require environment setup. Additionally, you'll need customize your configuration if you'd like to control Lamington's fine grained settings. Fortunately we've made it easy to get started with a simple boilerplate generation method;
Initially setting up a project can be tedious and repetitive, so we've created a simple CLI method to setup a boilerplate EOSIO project with Lamington integration.

```
lamington init
$ lamington init
```

This creates a `.lamingtonrc` file in your current directory which defaults to the latest available versions of EOS and EOS.CDT. The `.lamingtonrc` file allows you to configure additional settings using JSON syntax.
This creates a `.lamingtonrc` file in your current directory with default Lamington settings.

```
$ lamington init [PROJECT_NAME]
```

Optionally you can provide and additional `PROJECT_NAME` to create a project directory and initialize a boilerplate project within.

## Configuration

Lamington ships with a default configuration to make getting started simple and setup free. However, as your project grows, so will your need for additional Lamington configuration. For example, deployment to a testnet or the live network will require environment setup. Additionally, you'll need customize your configuration if you'd like to control Lamington's fine grained settings. Fortunately we've made a simple tool to get you started, simply run `lamington init` in your project directory to create a default `.lamingtonrc` configuration file.

### Using a Configuration File

- Configuring environments
- keepAlive
The `.lamingtonrc` file allows you to configure additional settings using JSON syntax. We're working on provide allot more settings, like defining multiple environments for each stage of your pipeline.

```
{
...
"keepAlive":true,
...
}
```

The `keepAlive` setting prevents Lamington from stopping the EOSIO container between each build, allowing you to develop faster and compile often.

## Resources

You can find more information about the Lamington tool-set and join our growing community of developers by visiting any of the following links;

[Examples](https://examples.lamington.io)

[API Documentation](https://api.lamington.io)

[Slack Channel](https://forms.gle/yTjNA46oKywaD7FR6)
Expand All @@ -125,8 +147,10 @@ You can find more information about the Lamington tool-set and join our growing
## Roadmap

### LamingtonJS
Core Lamington front end toolset

### Lamington-React
React context management for LamingtonJS

### Lamington-Angular

Expand Down
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,14 @@
},
"keywords": [
"EOS",
"Smart",
"Contract",
"Truffle"
"Smart Contract",
"Typescript",
"Truffle",
"Deployment",
"Testing",
"Mocha",
"Jade",
"Jest"
],
"author": "Kevin Brown",
"license": "MIT",
Expand Down
25 changes: 23 additions & 2 deletions src/accounts/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ import { AccountManager } from './accountManager';

export class Account {

/** EOSIO account name */
public name: string;
/** EOSIO account public key */
public publicKey: string;
/** EOSIO account private key */
public privateKey: string;
/** EOSIO account permissions */
public permissions: Permissions;

constructor(name: string, privateKey: string, publicKey?: string) {
// Store references
this.name = name;
this.privateKey = privateKey;
this.publicKey = publicKey || ecc.privateToPublic(privateKey);

// Set default permissions
this.permissions = {
active: {
actor: name,
Expand All @@ -27,6 +32,11 @@ export class Account {
};
}

/**
* Returns a configured active key permission
* @author Kevin Brown <github.com/thekevinbrown>
* @returns Valid active key
*/
public get active() {
return [
{
Expand All @@ -36,6 +46,11 @@ export class Account {
];
}

/**
* Returns a configured owner key permission
* @author Kevin Brown <github.com/thekevinbrown>
* @returns Valid owner key
*/
public get owner() {
return [
{
Expand All @@ -45,7 +60,13 @@ export class Account {
];
}

public addCodePermission = async (contract: Contract) => {
/**
* Adds the `eosio.code` permission to this account
* @author Kevin Brown <github.com/thekevinbrown>
* @author Mitch Pierias <github.com/MitchPierias>
* @returns Add permission transaction promise
*/
public addCodePermission = async () => {
await AccountManager.addCodePermission(this);
};
}
72 changes: 45 additions & 27 deletions src/accounts/accountManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,70 @@ import * as ecc from 'eosjs-ecc';
import { Account } from './account';
import { accountNameFromPublicKey } from './utils';
import { EOSManager } from '../eosManager';
import { Contract } from '../contracts';

interface AccountCreationOptions {
creator?: Account;
eos?: Api;
}

export class AccountManager {

/**
* Generates a new random account
* @note Shorthand method for [[AccountManager.createAccounts]]
* @author Kevin Brown <github.com/thekevinbrown>
* @param options Optional account creation settings
* @returns Result returned from [[AccountManager.createAccounts]]
*/
static createAccount = async (options?: AccountCreationOptions) => {
const [account] = await AccountManager.createAccounts(1, options);

return account;
};

/**
* Generates a specified number of random accounts
* @author Kevin Brown <github.com/thekevinbrown>
* @param numberOfAccounts Number of accounts to generate
* @returns Array of created account transaction promises
*/
static createAccounts = async (numberOfAccounts = 1, options?: AccountCreationOptions) => {
const accounts = [];

// Repeat account creation for specified
for (let i = 0; i < numberOfAccounts; i++) {
const privateKey = await ecc.unsafeRandomKey();
const publicKey = await ecc.privateToPublic(privateKey);
const accountName = accountNameFromPublicKey(publicKey);
const account = new Account(accountName, privateKey);

// Publish the new account and store result
await AccountManager.setupAccount(account, options);

accounts.push(account);
}

// Return created acounts
return accounts;
};

/**
* Publishes a new account and allocates ram where possible
* @author Kevin Brown <github.com/thekevinbrown>
* @param account [[Account]] to publish
* @param options Optional account settings
* @returns Transaction result promise
*/
static setupAccount = async (account: Account, options?: AccountCreationOptions) => {
const { creator, eos } = AccountManager.flattenOptions(options);

// Validate account contains required values
if (!account.name) throw new Error('Missing account name.');
if (!account.publicKey) throw new Error('Missing public key.');
if (!account.privateKey) throw new Error('Missing private key.');

// Configure the Signature Provider if available
if (EOSManager.signatureProvider) {
const nonLegacyPublicKey = convertLegacyPublicKey(account.publicKey);
EOSManager.signatureProvider.keys.set(nonLegacyPublicKey, account.privateKey);
EOSManager.signatureProvider.availableKeys.push(nonLegacyPublicKey);
}

// Get the system contract
const systemContract = await eos.getContract('eosio');

// Build account creation actions
const actions: any = [
{
account: 'eosio',
Expand Down Expand Up @@ -100,7 +118,6 @@ export class AccountManager {
},
});
}

// Same deal for delegatebw. Only if it's actually a thing.
if (systemContract.actions.has('delegatebw')) {
actions.push({
Expand All @@ -116,44 +133,42 @@ export class AccountManager {
},
});
}

// Execute the transaction
return await EOSManager.transact({ actions }, eos);
};

/**
* Grants `eosio.code` permission to the specified account's `active` key
* @note Should be moved to the `contracts/contract.ts` I think?
* @note Actually it is `account` based and not specific to contracts...
* @author Kevin Brown
* @author Mitch Pierias
* @author Kevin Brown <github.com/thekevinbrown>
* @author Mitch Pierias <github.com/MitchPierias>
* @param account Account without `eosio.code` permissions
*/
static addCodePermission = async (account: Account) => {
// We need to get their existing permissions, then add in a new eosio.code permission for this contract.
const { permissions } = await EOSManager.rpc.get_account(account.name);
const active = permissions.find((permission: any) => permission.perm_name == 'active');

const auth = active.required_auth;
const existingPermission = auth.accounts.find(
const { required_auth } = permissions.find((permission: any) => permission.perm_name == 'active');
// Check if `eosio.code` has already been set
const existingPermission = required_auth.accounts.find(
(account: any) =>
account.permission.actor === account.name &&
account.permission.permission === 'eosio.code'
);

// Throw if permission exists
if (existingPermission) {
throw new Error(
`Code permission is already present on account ${account.name} for contract ${
account.name
}`
);
}

// Add it in.
auth.accounts.push({
// Append the `eosio.code` permission to existing
required_auth.accounts.push({
permission: { actor: account.name, permission: 'eosio.code' },
weight: 1,
});

// Construct the update actions
const actions: any = [
{
account: 'eosio',
Expand All @@ -163,16 +178,19 @@ export class AccountManager {
account: account.name,
permission: 'active',
parent: 'owner',
auth,
auth: required_auth,
},
},
];

//console.log(JSON.stringify(actions, null, '\t'))

// Execute the transaction actions
await EOSManager.transact({ actions });
};

/**
* Flattens account creation options
* @author Kevin Brown <github.com/thekevinbrown>
* @returns Account creation options
*/
private static flattenOptions(options?: AccountCreationOptions) {
const creator = (options && options.creator) || EOSManager.adminAccount;
const eos = (options && options.eos) || EOSManager.api;
Expand Down
17 changes: 15 additions & 2 deletions src/accounts/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as ecc from 'eosjs-ecc';

export const accountNameFromPublicKey = (publicKey: string) => hashToEOSName(ecc.sha256(publicKey));
/** Digit pattern expression */
const digitPattern = /[06789]/g;

/** Digit mapping lookup */
const digitMapping: { [key: string]: string } = {
'0': '1',
'6': '2',
Expand All @@ -10,8 +12,19 @@ const digitMapping: { [key: string]: string } = {
'9': '5',
};

const digitPattern = /[06789]/g;
/**
* Generates an account name from the specified public key
* @author Kevin Brown <github.com/thekevinbrown>
* @param publicKey Valid EOSIO public key
* @returns EOSIO account name
*/
export const accountNameFromPublicKey = (publicKey: string) => hashToEOSName(ecc.sha256(publicKey));

/**
* Generates an account name from a hashed public key
* @author Kevin Brown <github.com/thekevinbrown>
* @returns EOSIO account name
*/
export const hashToEOSName = (data: string) =>
`l${data
.substring(0, 11)
Expand Down
Loading

0 comments on commit 2f21013

Please sign in to comment.