Hi everyone,
I think I may have detected bug on contract level after the hard fork. We at AMPnet have collection of contracts and some tests which were running fine before the fork. After the fork we made minor changes in contracts (only cosmetic ones) so that contracts could be compiled with new version of Sophia compiler. Contracts were deployed successfully but this time tests were failing.
We tried running them on top of local nodes(aeproject 1.0.2 + compiler) but also on top of officially available public compiler and testnet node. Results are the same. We have identified the issue, and it looks like require()
function is messing with Call.caller
value.
Therefore, we designed this tiny example that anyone should be able to reproduce.
RandomContract.aes
contract RandomContract =
entrypoint get_address() : address = ak_2cG1dC4Ad5ut67ZwkABTfN463gu9eiZCydhBXNyhmPUJyaNiak
BugExample.aes
contract RandomContractInterface =
entrypoint get_address : () => address
contract BugExample =
datatype event =
TestEvent(address)
entrypoint bug_test(remote: RandomContractInterface) =
Chain.event(TestEvent(Call.caller))
require(Call.caller != remote.get_address(), "This changes Call.caller value")
Chain.event(TestEvent(Call.caller))
bug_test
function emits two events with the value of Call.caller
. The value should be the same in both events (that was the case before hard fork). But it looks like require(...)
line messes with this value. When event values are decoded results are different.
Here’s test example:
const fs = require('fs');
const path = require('path');
const { Universal, Crypto } = require('@aeternity/aepp-sdk')
describe("Bug Test", () => {
it('might be a bug', async () => {
let client = await Universal({
url: "https://sdk-testnet.aepps.com",
internalUrl: "https://sdk-testnet.aepps.com",
keypair: {
publicKey: "ak_fUq2NesPXcYZ1CcqBcGC3StpdnQw3iVxMA3YSeCNAwfN4myQk",
secretKey: "7c6e602a94f30e4ea7edabe4376314f69ba7eaa2f355ecedb339df847b6f0d80575f81ffb0a297b7725dc671da0b1769b1fc5cbe45385c7b5ad1fc2eaf1d609d"
},
networkId: "ae_uat",
compilerUrl: "http://latest.compiler.aepps.com"
})
let randomContractSource = fs.readFileSync(path.join(__dirname, '..', 'contracts', 'RandomContract.aes'), 'utf8')
let randomContractInstance = await client.getContractInstance(randomContractSource)
let randomContractDeployTx = await randomContractInstance.deploy()
console.log("RandomContract deploy tx", randomContractDeployTx)
let bugExampleSource = fs.readFileSync(path.join(__dirname, '..', 'contracts', 'BugExample.aes'), 'utf8')
let bugExampleInstance = await client.getContractInstance(bugExampleSource)
let deployTx = await bugExampleInstance.deploy()
console.log("BugExample deploy tx", deployTx)
let testCall = await bugExampleInstance.methods.bug_test(randomContractDeployTx.address)
let beforeRequireEventValue = Crypto.addressFromDecimal(testCall.result.log[0].topics[1])
console.log("Log[0] Call.caller value", beforeRequireEventValue)
let afterRequireEventValue = Crypto.addressFromDecimal(testCall.result.log[1].topics[1])
console.log("Log[1] Call.caller value", afterRequireEventValue)
})
})
After running this test, following output is produced:
ampnet-ae-contracts git:(master) ✗ aeproject test --path test/BugTest.js
===== Starting Tests =====
Bug Test
RandomContract deploy tx { owner: 'ak_fUq2NesPXcYZ1CcqBcGC3StpdnQw3iVxMA3YSeCNAwfN4myQk',
transaction: 'th_EtsHpbj7SJhaRLDEydfNjKRXr5ukQbn3WjVSW9BLh7Vvx22wh',
address: 'ct_2jUBTA42RfeeHzYd8Hyq16opo5m7BVry9pc2zxgW1UXKh9QPv6',
createdAt: 2019-10-17T17:08:04.076Z,
result:
{ callerId: 'ak_fUq2NesPXcYZ1CcqBcGC3StpdnQw3iVxMA3YSeCNAwfN4myQk',
callerNonce: 300,
contractId: 'ct_2jUBTA42RfeeHzYd8Hyq16opo5m7BVry9pc2zxgW1UXKh9QPv6',
gasPrice: 1000000000,
gasUsed: 56,
height: 154943,
log: [],
returnType: 'ok',
returnValue: 'cb_Xfbg4g==' },
rawTx:
'tx_+QEkCwH4QrhAnb51Fz7usGBgM27RGs9r5klnpC6hnfNB1cYBB0cAkCQ7AJITcvfUThHeU5d/67ZNjrgxZXBqaqVLzn/OlQ3KDrjc+NoqAaEBV1+B/7Cil7dyXcZx2gsXabH8XL5FOFx7WtH8Lq8dYJ2CASy4kviQRgOgB6261C27UNQyYPL10R2Rn+9ppOcXh57YTEtBQId4qUjAuGO4QP5E1kQfADcANwAaDoI/AQM//tJZ97oANwBHAAEDnwCg08FqSWIcHWfUGYkQVVqnai2N4sjUrwJCobShuIV4TA6dLwIRRNZEHxFpbml0EdJZ97otZ2V0X2FkZHJlc3OCLwCFNC4wLjAAgwUAA4ZINsDLkAAAAACDGBf4hDuaygCHKxFE1kQfP+FYfH4=' }
BugExample deploy tx { owner: 'ak_fUq2NesPXcYZ1CcqBcGC3StpdnQw3iVxMA3YSeCNAwfN4myQk',
transaction: 'th_Tsj3CZ4pkrz514Ujz4oR4JLiCwhoX3psWxTSqbDwa5DRjgusc',
address: 'ct_2bXvoCbBHzdDs5GizsckyJ3T1X1hV2ZHAcW9ejpKcq5YJ7jqA5',
createdAt: 2019-10-17T17:08:11.947Z,
result:
{ callerId: 'ak_fUq2NesPXcYZ1CcqBcGC3StpdnQw3iVxMA3YSeCNAwfN4myQk',
callerNonce: 301,
contractId: 'ct_2bXvoCbBHzdDs5GizsckyJ3T1X1hV2ZHAcW9ejpKcq5YJ7jqA5',
gasPrice: 1000000000,
gasUsed: 56,
height: 154943,
log: [],
returnType: 'ok',
returnValue: 'cb_Xfbg4g==' },
rawTx:
'tx_+QHACwH4QrhAOBIhNBy74tlEKDAla+7KJSSA+sO2o7/N1gxTAR3vCysKPdsI1Zuz48Qg2IGoO/iEOLBieWdMaJ+LQYD6ZbEIBrkBd/kBdCoBoQFXX4H/sKKXt3JdxnHaCxdpsfxcvkU4XHta0fwurx1gnYIBLbkBK/kBKEYDoK9dwncrLlNmaT46X9EyL9EYEE/tyKCYeNBzW7gJawsFwLj7uMr+RNZEHwA3ADcAGg6CPwEDP/5lpeAPAjcBhwE3AUcANwBGNAAADAOfAYGBsnETzY6sSLzsVWOTq0rhDZJgAknMsDPbq286C2kz+QwDX2IAAQM//mzEoF8ANwFHAjcAVQBE/BMCAAICAxFlpeAPGgJvgibPDAMADAEAAwD8EdJZ97o3AEcAVQAjAAcMCgwDeVRoaXMgY2hhbmdlcyBDYWxsLmNhbGxlciB2YWx1ZfsAGgJvgibPVQBE/BMCAAIEAxFlpeAPDAM/BgMIqy8DEUTWRB8RaW5pdBFlpeAPLUNoYWluLmV2ZW50EWzEoF8hYnVnX3Rlc3SCLwCFNC4wLjAAgwUAA4ZLCIcxqAAAAACDGBf4hDuaygCHKxFE1kQfP5CYZBg=' }
Log[0] Call.caller value ak_2jUBTA42RfeeHzYd8Hyq16opo5m7BVry9pc2zxgW1UXKh9QPv6
Log[1] Call.caller value ak_fUq2NesPXcYZ1CcqBcGC3StpdnQw3iVxMA3YSeCNAwfN4myQk
So in one log entry Call.caller
value is correct (ak_fUq…) but in another it’s different which is something we found weird. Not only is another value different, but it is actually equal to address of deployed RandomContract. Take a closer look on output of RandomContract deploy transaction data. Contract id is ct_2jUB… which is exactly the value of wrong Call.caller
from decoded logs (ak_2jUB…).
We didn’t know if this behaviour is intentional because something critical has changed with the new hard fork in the working of either require()
function or Call.caller
value, or in the other case, this behaviour represents a bug.
Can someone check these examples and shed some light on what could be the issue here?
Thanks