Skip to content

Commit

Permalink
feat: support new delegation format in Ceres
Browse files Browse the repository at this point in the history
  • Loading branch information
davidyuk committed Jan 17, 2024
1 parent b2653eb commit 786e954
Show file tree
Hide file tree
Showing 26 changed files with 703 additions and 231 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ coverage.*
site
/src/apis/
/src/tx/builder/schema.generated.ts
/src/tx/builder/delegation/schema.generated.ts
/tooling/autorest/compiler-swagger.yaml
/tooling/autorest/middleware-openapi.yaml
/test/environment/ledger/browser
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: '3'
services:
node:
image: aeternity/aeternity:v6.12.0
image: aeternity/aeternity:master@sha256:dac4a3b032ee05f2e0d4b4ce5b0da5c639f95e849fe0332e393f87ca01da0df2
hostname: node
ports: ["3013:3013", "3113:3113", "3014:3014", "3114:3114"]
volumes:
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/aens.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ const contractAddress = 'ct_asd2ks...'
const name = 'example.chain'

// this signature will allow the contract to perform a pre-claim on your behalf
const preClaimSig = await aeSdk.signDelegationToContract(contractAddress)
const preClaimSig = await aeSdk.signDelegationToContract(contractAddress, { isOracle: false })

// this signature will allow the contract to perform
// any name related transaction for a specific name that you own
Expand Down
36 changes: 23 additions & 13 deletions examples/browser/aepp/src/DelegationSignature.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,33 @@
</div>
<div>
<label>
<input v-model="type" type="radio" value="general">
AENS preclaim and oracle
<input v-model="type" type="radio" :value="DelegationTag.AensPreclaim">
AENS preclaim
</label>
</div>
<div>
<label>
<input v-model="type" type="radio" value="name">
<input v-model="type" type="radio" :value="DelegationTag.Oracle">
Oracle
</label>
</div>
<div>
<label>
<input v-model="type" type="radio" :value="DelegationTag.AensName">
AENS name
</label>
<div><input v-model="name"></div>
</div>
<div>
<label>
<input v-model="type" type="radio" value="all-names">
<input v-model="type" type="radio" :value="DelegationTag.AensWildcard">
All AENS names
</label>
</div>
<div>
<label>
<input v-model="type" type="radio" value="oracle-query">
Oracle query
<input v-model="type" type="radio" :value="DelegationTag.OracleResponse">
Response to oracle query
</label>
<div><input v-model="oracleQueryId"></div>
</div>
Expand All @@ -43,12 +49,14 @@

<script>
import { mapState } from 'vuex';
import { DelegationTag } from '@aeternity/aepp-sdk';
import Value from './components/Value.vue';
export default {
components: { Value },
data: () => ({
type: 'general',
DelegationTag,
type: DelegationTag.AensPreclaim,
contractAddress: 'ct_6y3N9KqQb74QsvR9NrESyhWeLNiA9aJgJ7ua8CvsTuGot6uzh',
name: 'test.chain',
oracleQueryId: 'oq_6y3N9KqQb74QsvR9NrESyhWeLNiA9aJgJ7ua8CvsTuGot6uzh',
Expand All @@ -58,17 +66,19 @@ export default {
methods: {
sign() {
switch (this.type) {
case 'general':
return this.aeSdk.signDelegationToContract(this.contractAddress);
case 'name':
case DelegationTag.AensPreclaim:
return this.aeSdk.signDelegationToContract(this.contractAddress, { isOracle: false });
case DelegationTag.Oracle:
return this.aeSdk.signDelegationToContract(this.contractAddress, { isOracle: true });
case DelegationTag.AensName:
return this.aeSdk.signNameDelegationToContract(this.contractAddress, this.name);
case 'all-names':
case DelegationTag.AensWildcard:
return this.aeSdk.signAllNamesDelegationToContract(this.contractAddress);
case 'oracle-query':
case DelegationTag.OracleResponse:
return this.aeSdk
.signOracleQueryDelegationToContract(this.contractAddress, this.oracleQueryId);
default:
throw new Error(`Unknown delegation signature type: ${this.type}`)
throw new Error(`Unknown delegation signature type: ${DelegationTag[this.type]}`);
}
},
},
Expand Down
10 changes: 9 additions & 1 deletion examples/browser/wallet-iframe/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
import {
MemoryAccount, generateKeyPair, AeSdkWallet, Node, CompilerHttp,
BrowserWindowMessageConnection, METHODS, WALLET_TYPE, RPC_STATUS,
RpcConnectionDenyError, RpcRejectedByUserError, unpackTx,
RpcConnectionDenyError, RpcRejectedByUserError, unpackTx, unpackDelegation,
} from '@aeternity/aepp-sdk';
import { TypeResolver, ContractByteArrayEncoder } from '@aeternity/aepp-calldata';
import Value from './Value.vue';
Expand Down Expand Up @@ -230,6 +230,14 @@ export default {
return super.signOracleQueryDelegationToContract(contractAddress, oracleQueryId, options);
}

async signDelegation(delegation, { aeppRpcClientId: id, aeppOrigin, ...options }) {
if (id != null) {
const opt = { ...options, ...unpackDelegation(delegation) };
genConfirmCallback('sign delegation')(id, opt, aeppOrigin);
}
return super.signDelegation(delegation, options);
}

static generate() {
// TODO: can inherit parent method after implementing https://github.com/aeternity/aepp-sdk-js/issues/1672
return new AccountMemoryProtected(generateKeyPair().secretKey);
Expand Down
10 changes: 9 additions & 1 deletion examples/browser/wallet-web-extension/src/background.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import browser from 'webextension-polyfill';
import {
AeSdkWallet, CompilerHttp, Node, MemoryAccount, generateKeyPair, BrowserRuntimeConnection,
WALLET_TYPE, RpcConnectionDenyError, RpcRejectedByUserError, unpackTx,
WALLET_TYPE, RpcConnectionDenyError, RpcRejectedByUserError, unpackTx, unpackDelegation,
} from '@aeternity/aepp-sdk';
import { TypeResolver, ContractByteArrayEncoder } from '@aeternity/aepp-calldata';

Expand Down Expand Up @@ -124,6 +124,14 @@ class AccountMemoryProtected extends MemoryAccount {
return super.signOracleQueryDelegationToContract(contractAddress, oracleQueryId, options);
}

async signDelegation(delegation, { aeppRpcClientId: id, aeppOrigin, ...options }) {
if (id != null) {
const opt = { ...options, ...unpackDelegation(delegation) };
await genConfirmCallback('sign delegation')(id, opt, aeppOrigin);
}
return super.signDelegation(delegation, options);
}

static generate() {
// TODO: can inherit parent method after implementing https://github.com/aeternity/aepp-sdk-js/issues/1672
return new AccountMemoryProtected(generateKeyPair().secretKey);
Expand Down
30 changes: 30 additions & 0 deletions src/AeSdkBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,25 @@ export default class AeSdkBase extends AeSdkMethods {
return this._resolveAccount(onAccount).signTypedData(data, aci, options);
}

/**
* @deprecated use AeSdkBase:signDelegation in Ceres
*/
async signDelegationToContract(
contractAddress: Encoded.ContractAddress,
{ onAccount, ...options }: { onAccount?: OnAccount }
& Parameters<AccountBase['signDelegationToContract']>[1] = {},
): Promise<Encoded.Signature> {
options.networkId ??= this.selectedNodeName !== null
? await this.api.getNetworkId() : undefined;
options.consensusProtocolVersion ??= this.selectedNodeName !== null
? (await this.api.getNodeInfo()).consensusProtocolVersion : undefined;
return this._resolveAccount(onAccount)
.signDelegationToContract(contractAddress, options);
}

/**
* @deprecated use AeSdkBase:signDelegation in Ceres
*/
async signNameDelegationToContract(
contractAddress: Encoded.ContractAddress,
name: AensName,
Expand All @@ -196,21 +204,31 @@ export default class AeSdkBase extends AeSdkMethods {
): Promise<Encoded.Signature> {
options.networkId ??= this.selectedNodeName !== null
? await this.api.getNetworkId() : undefined;
options.consensusProtocolVersion ??= this.selectedNodeName !== null
? (await this.api.getNodeInfo()).consensusProtocolVersion : undefined;
return this._resolveAccount(onAccount)
.signNameDelegationToContract(contractAddress, name, options);
}

/**
* @deprecated use AeSdkBase:signDelegation in Ceres
*/
async signAllNamesDelegationToContract(
contractAddress: Encoded.ContractAddress,
{ onAccount, ...options }: { onAccount?: OnAccount }
& Parameters<AccountBase['signAllNamesDelegationToContract']>[1] = {},
): Promise<Encoded.Signature> {
options.networkId ??= this.selectedNodeName !== null
? await this.api.getNetworkId() : undefined;
options.consensusProtocolVersion ??= this.selectedNodeName !== null
? (await this.api.getNodeInfo()).consensusProtocolVersion : undefined;
return this._resolveAccount(onAccount)
.signAllNamesDelegationToContract(contractAddress, options);
}

/**
* @deprecated use AeSdkBase:signDelegation in Ceres
*/
async signOracleQueryDelegationToContract(
contractAddress: Encoded.ContractAddress,
oracleQueryId: Encoded.OracleQueryId,
Expand All @@ -219,10 +237,22 @@ export default class AeSdkBase extends AeSdkMethods {
): Promise<Encoded.Signature> {
options.networkId ??= this.selectedNodeName !== null
? await this.api.getNetworkId() : undefined;
options.consensusProtocolVersion ??= this.selectedNodeName !== null
? (await this.api.getNodeInfo()).consensusProtocolVersion : undefined;
return this._resolveAccount(onAccount)
.signOracleQueryDelegationToContract(contractAddress, oracleQueryId, options);
}

async signDelegation(
delegation: Encoded.Bytearray,
{ onAccount, ...options }: { onAccount?: OnAccount }
& Parameters<AccountBase['signDelegation']>[1] = {},
): Promise<Encoded.Signature> {
options.networkId ??= this.selectedNodeName !== null
? await this.api.getNetworkId() : undefined;
return this._resolveAccount(onAccount).signDelegation(delegation, options);
}

/**
* The same as AeSdkMethods:getContext, but it would resolve ak_-prefixed address in
* `mergeWith.onAccount` to AccountBase.
Expand Down
12 changes: 10 additions & 2 deletions src/AeSdkWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,14 @@ export default class AeSdkWallet extends AeSdk {
};
},
[METHODS.signDelegationToContract]: async ({
contractAddress, name, oracleQueryId, allNames, onAccount = this.address,
contractAddress, name, oracleQueryId, allNames, onAccount = this.address, isOracle,
}, origin) => {
if (!this._isRpcClientConnected(id)) throw new RpcNotAuthorizeError();
if (!this.addresses().includes(onAccount)) {
throw new RpcPermissionDenyError(onAccount);
}

isOracle ??= false;
const parameters = { onAccount, aeppOrigin: origin, aeppRpcClientId: id };
const signature = await (
(name == null ? null : this
Expand All @@ -317,10 +318,17 @@ export default class AeSdkWallet extends AeSdk {
.signOracleQueryDelegationToContract(contractAddress, oracleQueryId, parameters))
?? (allNames !== true ? null : this
.signAllNamesDelegationToContract(contractAddress, parameters))
?? this.signDelegationToContract(contractAddress, parameters)
?? this.signDelegationToContract(contractAddress, { ...parameters, isOracle })
);
return { signature };
},
[METHODS.signDelegation]: async ({ delegation, onAccount = this.address }, origin) => {
if (!this._isRpcClientConnected(id)) throw new RpcNotAuthorizeError();
if (!this.addresses().includes(onAccount)) throw new RpcPermissionDenyError(onAccount);
const parameters = { onAccount, aeppOrigin: origin, aeppRpcClientId: id };
const signature = await this.signDelegation(delegation, parameters);
return { signature };
},
},
),
};
Expand Down
34 changes: 33 additions & 1 deletion src/account/Base.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Encoded } from '../utils/encoder';
import Node from '../Node';
import CompilerBase from '../contract/compiler/Base';
import { AensName, Int } from '../tx/builder/constants';
import { AensName, ConsensusProtocolVersion, Int } from '../tx/builder/constants';
import { AciValue, Domain } from '../utils/typed-data';
import { NotImplementedError } from '../utils/errors';

Expand Down Expand Up @@ -80,7 +80,10 @@ export default abstract class AccountBase {
* Sign delegation of AENS, oracle operations to a contract
* @param contractAddress - Address of a contract to delegate permissions to
* @param options - Options
* @param options.isOracle - Use to generate an oracle delegation signature in Ceres, otherwise an
* AENS preclaim delegation signature would be generated
* @returns Signature
* @deprecated use AccountBase:signDelegation in Ceres
*/
// TODO: make abstract in the next major release
// eslint-disable-next-line class-methods-use-this
Expand All @@ -89,6 +92,8 @@ export default abstract class AccountBase {
contractAddress: Encoded.ContractAddress,
options?: {
networkId?: string;
consensusProtocolVersion?: ConsensusProtocolVersion;
isOracle?: boolean;
aeppOrigin?: string;
aeppRpcClientId?: string;
},
Expand All @@ -103,6 +108,7 @@ export default abstract class AccountBase {
* @param name - AENS name to manage by a contract
* @param options - Options
* @returns Signature
* @deprecated use AccountBase:signDelegation in Ceres
*/
// TODO: make abstract in the next major release
// eslint-disable-next-line class-methods-use-this
Expand All @@ -112,6 +118,7 @@ export default abstract class AccountBase {
name: AensName,
options?: {
networkId?: string;
consensusProtocolVersion?: ConsensusProtocolVersion;
aeppOrigin?: string;
aeppRpcClientId?: string;
},
Expand All @@ -125,6 +132,7 @@ export default abstract class AccountBase {
* @param contractAddress - Address of a contract to delegate permissions to
* @param options - Options
* @returns Signature
* @deprecated use AccountBase:signDelegation in Ceres
*/
// TODO: make abstract in the next major release
// eslint-disable-next-line class-methods-use-this
Expand All @@ -133,6 +141,7 @@ export default abstract class AccountBase {
contractAddress: Encoded.ContractAddress,
options?: {
networkId?: string;
consensusProtocolVersion?: ConsensusProtocolVersion;
aeppOrigin?: string;
aeppRpcClientId?: string;
},
Expand All @@ -154,6 +163,7 @@ export default abstract class AccountBase {
* @param oracleQueryId - Oracle query ID to reply by a contract
* @param options - Options
* @returns Signature
* @deprecated use AccountBase:signDelegation in Ceres
*/
// TODO: make abstract in the next major release
// eslint-disable-next-line class-methods-use-this
Expand All @@ -163,6 +173,7 @@ export default abstract class AccountBase {
oracleQueryId: Encoded.OracleQueryId,
options?: {
networkId?: string;
consensusProtocolVersion?: ConsensusProtocolVersion;
aeppOrigin?: string;
aeppRpcClientId?: string;
},
Expand All @@ -183,4 +194,25 @@ export default abstract class AccountBase {
* Account address
*/
readonly address!: Encoded.AccountAddress;

/**
* Sign delegation, works only in Ceres
* @param delegation - Delegation to sign
* @param options - Options
* @returns Signature
*/
// TODO: make abstract in the next major release
// eslint-disable-next-line class-methods-use-this
async signDelegation(
/* eslint-disable @typescript-eslint/no-unused-vars */
delegation: Encoded.Bytearray,
options?: {
networkId?: string;
aeppOrigin?: string;
aeppRpcClientId?: string;
},
/* eslint-enable @typescript-eslint/no-unused-vars */
): Promise<Encoded.Signature> {
throw new NotImplementedError('signDelegation method');

Check warning on line 216 in src/account/Base.ts

View check run for this annotation

Codecov / codecov/patch

src/account/Base.ts#L216

Added line #L216 was not covered by tests
}
}
5 changes: 5 additions & 0 deletions src/account/Generalized.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export default class AccountGeneralized extends AccountBase {
throw new NotImplementedError('signing delegation to contract using generalized account');
}

// eslint-disable-next-line class-methods-use-this
override async signDelegation(): Promise<Encoded.Signature> {
throw new NotImplementedError('signing delegation using generalized account');

Check warning on line 68 in src/account/Generalized.ts

View check run for this annotation

Codecov / codecov/patch

src/account/Generalized.ts#L68

Added line #L68 was not covered by tests
}

override async signTransaction(
tx: Encoded.Transaction,
{ authData, onCompiler, onNode }: Parameters<AccountBase['signTransaction']>[1],
Expand Down
5 changes: 5 additions & 0 deletions src/account/Ledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ export default class AccountLedger extends AccountBase {
throw new NotImplementedError('signing delegation to contract using Ledger HW');
}

// eslint-disable-next-line class-methods-use-this
override async signDelegation(): Promise<Encoded.Signature> {
throw new NotImplementedError('signing delegation using Ledger HW');

Check warning on line 70 in src/account/Ledger.ts

View check run for this annotation

Codecov / codecov/patch

src/account/Ledger.ts#L70

Added line #L70 was not covered by tests
}

override async signTransaction(
tx: Encoded.Transaction,
{ innerTx, networkId }: { innerTx?: boolean; networkId?: string } = {},
Expand Down
Loading

0 comments on commit 786e954

Please sign in to comment.