In smart contracts, AENS delegated signature problem
Closed this issue · 5 comments
@davidyuk should know !
AENS.transfer
still works, but signature format changed since Ceres.
As far as we all in Ceres the best to use signDelegation
method of Account or AeSdk.
The error you have is because aepp-calldata expects to get signature as hex string or Buffer, but sdk encodes it as sg_
. So, you need to decode
signature before passing to calldata. Related issue: aeternity/aepp-calldata-js#240
Here is example of changing name owner via delegation signature in Ceres using the sdk@13.3.2
import {
Node, AeSdk, MemoryAccount, CompilerHttp, DelegationTag, packDelegation, decode,
} from '@aeternity/aepp-sdk';
const [owner, recipient] = [MemoryAccount.generate(), MemoryAccount.generate()];
console.log('Owner address', owner.address);
console.log('Recipient address', recipient.address);
const aeSdk = new AeSdk({
accounts: [owner],
nodes: [{ name: 'testnet', instance: new Node('https://testnet.aeternity.io') }],
onCompiler: new CompilerHttp('https://v7.compiler.aepps.com'),
});
await fetch(`https://faucet.aepps.com/account/${owner.address}`, { method: 'POST' });
const name = `test-name-${Date.now()}.chain`;
await aeSdk.aensClaim(name, 0);
console.log('Claimed name', name);
console.log('Name owner', (await aeSdk.aensQuery(name)).owner);
const contractSourceCode = `
contract DelegateTest =
stateful entrypoint transfer(
owner: address, new_owner: address, name: string, sign: signature): unit =
AENS.transfer(owner, new_owner, name, signature = sign)
`;
const contract = await aeSdk.initializeContract({ sourceCode: contractSourceCode });
const contractAddress = (await contract.$deploy([])).address;
console.log('Contract deployed at', contractAddress);
const delegation = packDelegation({
tag: DelegationTag.AensName, accountAddress: aeSdk.address, contractAddress, nameId: name,
});
const delegationSignature = decode(await aeSdk.signDelegation(delegation));
await contract.transfer(owner.address, recipient.address, name, delegationSignature);
console.log('Name transferred');
console.log('Name owner', (await aeSdk.aensQuery(name)).owner);
A private key exposed in the above message, ensure that you don't have much funds there!
I figured out the issue. Contracts deployed in Iris are executed in FATE v2, so it expects an old signature format. The problem is that we have security concerns about that format, and I would like to drop its support in future SDK versions. Is it feasible to migrate your contracts to FATE v3?
Meanwhile, you can provide consensusProtocolVersion: ConsensusProtocolVersion.Iris
option to generate an old signature in Ceres using AeSdk::signNameDelegationToContract
. See the below reproduction.
The code I've used to test it
import {
Node, AeSdk, MemoryAccount, CompilerHttp, ConsensusProtocolVersion, decode,
} from '@aeternity/aepp-sdk';
const [owner, recipient] = [
new MemoryAccount('9ebd7beda0c79af72a42ece3821a56eff16359b6df376cf049aee995565f022f840c974b97164776454ba119d84edc4d6058a8dec92b6edc578ab2d30b4c4200'),
MemoryAccount.generate(),
];
console.log('Owner address', owner.address);
console.log('Recipient address', recipient.address);
const aeSdk = new AeSdk({
accounts: [owner],
nodes: [{ name: 'testnet', instance: new Node('http://localhost:3013') }],
onCompiler: new CompilerHttp('https://v7.compiler.aepps.com'),
});
const contractSourceCode = `
contract DelegateTest =
stateful entrypoint transfer(
owner: address, new_owner: address, name: string, sign: signature): unit =
AENS.transfer(owner, new_owner, name, signature = sign)
`;
const contract = await aeSdk.initializeContract({ sourceCode: contractSourceCode });
const contractAddress = (await contract.$deploy([])).address;
console.log('Contract deployed at', contractAddress);
console.log('Height', await aeSdk.getHeight());
console.log('Consensus', ConsensusProtocolVersion[(await aeSdk.api.getNodeInfo()).consensusProtocolVersion]);
const name = `test-name-${Date.now()}.chain`;
const nameObj = await aeSdk.aensPreclaim(name);
await nameObj.claim();
console.log('Claimed name', name);
console.log('Name owner', (await aeSdk.aensQuery(name)).owner);
console.log('Height', await aeSdk.getHeight());
console.log('Consensus', ConsensusProtocolVersion[(await aeSdk.api.getNodeInfo()).consensusProtocolVersion]);
const delegationSignature = await aeSdk.signNameDelegationToContract(
contractAddress,
name,
{ consensusProtocolVersion: ConsensusProtocolVersion.Iris },
);
console.log('delegationSignature', delegationSignature);
const delegationSignatureAsHex = decode(delegationSignature);
await contract.transfer(owner.address, recipient.address, name, delegationSignatureAsHex);
console.log('Name transferred');
console.log('Name owner', (await aeSdk.aensQuery(name)).owner);
dev_mode:
auto_emit_microblocks: true
chain:
persist: false
hard_forks:
"1": 0
"5": 1
"6": 2
genesis_accounts:
ak_21A27UVVt3hDkBE5J7rhhqnH5YNb4Y1dqo4PnSybrH85pnWo7E: 10000000000000000000000
So, the contract deployed in Iris and called in Ceres.