Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix code check failure in parallel #3384

Merged
merged 15 commits into from
Mar 29, 2023
10 changes: 5 additions & 5 deletions contract/AElf.Contracts.Genesis/BasicContractZero.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public override Address DeploySystemSmartContract(SystemContractDeploymentInput
var transactionMethodCallList = input.TransactionMethodCallList;

// Context.Sender should be identical to Genesis contract address before initialization in production
var address = DeploySmartContract(name, category, code, true, Context.Sender);
var address = DeploySmartContract(name, category, code, true, Context.Sender, false);

if (transactionMethodCallList != null)
foreach (var methodCall in transactionMethodCallList.Value)
Expand Down Expand Up @@ -282,7 +282,7 @@ public override Address DeploySmartContract(ContractDeploymentInput input)

var address =
DeploySmartContract(null, input.Category, input.Code.ToByteArray(), false,
DecideNonSystemContractAuthor(contractProposingInput?.Proposer, Context.Sender));
DecideNonSystemContractAuthor(contractProposingInput?.Proposer, Context.Sender), false);
return address;
}

Expand All @@ -296,7 +296,7 @@ public override Address UpdateSmartContract(ContractUpdateInput input)
if (!TryClearContractProposingData(inputHash, out _))
Assert(Context.Sender == info.Author, "No permission.");

UpdateSmartContract(contractAddress, input.Code.ToByteArray(), info.Author);
UpdateSmartContract(contractAddress, input.Code.ToByteArray(), info.Author, false);

return contractAddress;
}
Expand Down Expand Up @@ -448,7 +448,7 @@ public override Address PerformDeployUserSmartContract(ContractDeploymentInput i
TryClearContractProposingData(inputHash, out var contractProposingInput);

var address = DeploySmartContract(null, input.Category, input.Code.ToByteArray(), false,
contractProposingInput.Author);
contractProposingInput.Author, true);
return address;
}

Expand All @@ -459,7 +459,7 @@ public override Empty PerformUpdateUserSmartContract(ContractUpdateInput input)
var inputHash = CalculateHashFromInput(input);
TryClearContractProposingData(inputHash, out var proposingInput);

UpdateSmartContract(input.Address, input.Code.ToByteArray(), proposingInput.Author);
UpdateSmartContract(input.Address, input.Code.ToByteArray(), proposingInput.Author, true);

return new Empty();
}
Expand Down
14 changes: 9 additions & 5 deletions contract/AElf.Contracts.Genesis/BasicContractZero_Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace AElf.Contracts.Genesis;
public partial class BasicContractZero
{
private Address DeploySmartContract(Hash name, int category, byte[] code, bool isSystemContract,
Address author)
Address author, bool isUserContract)
{
if (name != null)
Assert(State.NameAddressMapping[name] == null, "contract name has already been registered before");
Expand All @@ -33,7 +33,8 @@ private Address DeploySmartContract(Hash name, int category, byte[] code, bool i
Category = category,
CodeHash = codeHash,
IsSystemContract = isSystemContract,
Version = 1
Version = 1,
IsUserContract = isUserContract
};

var reg = new SmartContractRegistration
Expand All @@ -43,7 +44,8 @@ private Address DeploySmartContract(Hash name, int category, byte[] code, bool i
CodeHash = codeHash,
IsSystemContract = info.IsSystemContract,
Version = info.Version,
ContractAddress = contractAddress
ContractAddress = contractAddress,
IsUserContract = isUserContract
};

var contractInfo = Context.DeploySmartContract(contractAddress, reg, name);
Expand Down Expand Up @@ -78,7 +80,7 @@ private Address DeploySmartContract(Hash name, int category, byte[] code, bool i
return contractAddress;
}

private void UpdateSmartContract(Address contractAddress, byte[] code, Address author)
private void UpdateSmartContract(Address contractAddress, byte[] code, Address author, bool isUserContract)
{
var info = State.ContractInfos[contractAddress];
Assert(info != null, "Contract not found.");
Expand All @@ -90,6 +92,7 @@ private void UpdateSmartContract(Address contractAddress, byte[] code, Address a
AssertContractExists(newCodeHash);

info.CodeHash = newCodeHash;
info.IsUserContract = isUserContract;
info.Version++;

var reg = new SmartContractRegistration
Expand All @@ -99,7 +102,8 @@ private void UpdateSmartContract(Address contractAddress, byte[] code, Address a
CodeHash = newCodeHash,
IsSystemContract = info.IsSystemContract,
Version = info.Version,
ContractAddress = contractAddress
ContractAddress = contractAddress,
IsUserContract = isUserContract
};

var contractInfo = Context.UpdateSmartContract(contractAddress, reg, null, info.ContractVersion);
Expand Down
2 changes: 2 additions & 0 deletions protobuf/acs0.proto
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ message ContractInfo
// The version of the current contract.
int32 version = 6;
string contract_version = 7;
// Indicates if the contract is the user contract.
bool is_user_contract = 8;
}

message ContractDeploymentInput {
Expand Down
2 changes: 2 additions & 0 deletions protobuf/aelf/core.proto
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ message SmartContractRegistration {
string contract_version = 6;
// The address of the current contract.
Address contract_address = 7;
// Indicates if the contract is the user contract.
bool is_user_contract = 8;
}

message TransactionExecutingStateSet {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ internal class CodeCheckValidationProvider : IBlockValidationProvider
private readonly ICheckedCodeHashProvider _checkedCodeHashProvider;
private readonly IContractReaderFactory<ACS0Container.ACS0Stub> _contractReaderFactory;
private readonly ISmartContractAddressService _smartContractAddressService;
private readonly ICodeCheckService _codeCheckService;

public CodeCheckValidationProvider(ISmartContractAddressService smartContractAddressService,
gldeng marked this conversation as resolved.
Show resolved Hide resolved
IContractReaderFactory<ACS0Container.ACS0Stub> contractReaderFactory,
ICheckedCodeHashProvider checkedCodeHashProvider,
IOptionsSnapshot<CodeCheckOptions> codeCheckOptions)
IOptionsSnapshot<CodeCheckOptions> codeCheckOptions, ICodeCheckService codeCheckService)
{
_smartContractAddressService = smartContractAddressService;
_contractReaderFactory = contractReaderFactory;
_checkedCodeHashProvider = checkedCodeHashProvider;
_codeCheckService = codeCheckService;

Logger = NullLogger<CodeCheckValidationProvider>.Instance;
}
Expand Down Expand Up @@ -52,18 +54,37 @@ public async Task<bool> ValidateBlockAfterExecuteAsync(IBlock block)
BlockHeight = block.Header.Height,
ContractAddress = genesisContractAddress
}).GetContractCodeHashListByDeployingBlockHeight.CallAsync(new Int64Value { Value = block.Header.Height });

if (codeHashList == null || !codeHashList.Value.Any())
foreach (var codeHash in codeHashList.Value)
{
Logger.LogInformation("CodeHashList is empty.");
return true;
if (_checkedCodeHashProvider.IsCodeHashExists(new BlockIndex
{
BlockHash = blockHash,
BlockHeight = block.Header.Height
}, codeHash))
{
continue;
}

var contractRegistration = await _contractReaderFactory.Create(new ContractReaderContext
{
BlockHash = blockHash,
BlockHeight = block.Header.Height,
ContractAddress = genesisContractAddress
}).GetSmartContractRegistrationByCodeHash.CallAsync(codeHash);

if (await _codeCheckService.PerformCodeCheckAsync(contractRegistration.Code.ToByteArray(),
blockHash, block.Header.Height, contractRegistration.Category,
contractRegistration.IsSystemContract, contractRegistration.IsUserContract))
{
continue;
zhxymh marked this conversation as resolved.
Show resolved Hide resolved
}

Logger.LogWarning("Code check validate failed. block hash: {BlockHash}, code hash: {CodeHash}", blockHash.ToHex(),
codeHash.ToHex());
return false;
}

Logger.LogInformation("block hash: {Block}", block);
return codeHashList.Value.All(codeHash => _checkedCodeHashProvider.IsCodeHashExists(new BlockIndex
{
BlockHash = blockHash,
BlockHeight = block.Header.Height
}, codeHash));
return true;
}
}
3 changes: 3 additions & 0 deletions src/AElf.Kernel.CodeCheck/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("AElf.Kernel.CodeCheck.Tests")]
6 changes: 6 additions & 0 deletions test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ public async Task DeploySmartContracts_Test()
nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.GetContractInfo), deployAddress));
contractInfo.Version.ShouldBe(1);
contractInfo.Author.ShouldBe(BasicContractZeroAddress);
contractInfo.IsUserContract.ShouldBeFalse();
contractInfo.ContractVersion.ShouldBe("1.0.0.0");

{
Expand Down Expand Up @@ -520,6 +521,7 @@ public async Task UpdateSmartContract_Test()
nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.GetContractInfo), newAddress));
updateContractInfo.Version.ShouldBe(contractInfo.Version + 1);
updateContractInfo.ContractVersion.ShouldBe("1.2.0.0");
updateContractInfo.IsUserContract.ShouldBeFalse();

var codeThird = Codes.Single(kv => kv.Key.Contains("TestContract.BasicSecurity")).Value;
contractUpdateInput = new ContractUpdateInput
Expand Down Expand Up @@ -1677,13 +1679,15 @@ public async Task DeployAndUpdateUserSmartContracts_Success_Test()
var registration = await SideChainTester.CallContractMethodAsync(SideBasicContractZeroAddress,
nameof(ACS0Container.ACS0Stub.GetSmartContractRegistrationByCodeHash), codeHash);
var smartContractRegistration = SmartContractRegistration.Parser.ParseFrom(registration);
smartContractRegistration.IsUserContract.ShouldBeTrue();
smartContractRegistration.IsSystemContract.ShouldBeFalse();
smartContractRegistration.ContractAddress.ShouldBe(contractDeployed.Address);
smartContractRegistration.CodeHash.ShouldBe(codeHash);

var info = await SideChainTester.CallContractMethodAsync(SideBasicContractZeroAddress,
nameof(ACS0Container.ACS0Stub.GetContractInfo), contractDeployed.Address);
var contractInfo = ContractInfo.Parser.ParseFrom(info);
contractInfo.IsUserContract.ShouldBeTrue();
contractInfo.IsSystemContract.ShouldBeFalse();
contractInfo.CodeHash.ShouldBe(codeHash);
contractInfo.Author.ShouldBe(Address.FromPublicKey(CreatorKeyPair.PublicKey));
Expand Down Expand Up @@ -1743,12 +1747,14 @@ public async Task DeployAndUpdateUserSmartContracts_Success_Test()
info = await SideChainTester.CallContractMethodAsync(SideBasicContractZeroAddress,
nameof(ACS0Container.ACS0Stub.GetContractInfo), contractDeployed.Address);
contractInfo = ContractInfo.Parser.ParseFrom(info);
contractInfo.IsUserContract.ShouldBeTrue();
contractInfo.IsSystemContract.ShouldBeFalse();
contractInfo.Author.ShouldBe(Address.FromPublicKey(CreatorKeyPair.PublicKey));

registration = await SideChainTester.CallContractMethodAsync(SideBasicContractZeroAddress,
nameof(ACS0Container.ACS0Stub.GetSmartContractRegistrationByCodeHash), contractInfo.CodeHash);
smartContractRegistration = SmartContractRegistration.Parser.ParseFrom(registration);
smartContractRegistration.IsUserContract.ShouldBeTrue();
smartContractRegistration.IsSystemContract.ShouldBeFalse();
smartContractRegistration.ContractAddress.ShouldBe(contractDeployed.Address);
smartContractRegistration.CodeHash.ShouldBe(contractInfo.CodeHash);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AElf.Kernel.Blockchain.Application;
using AElf.Kernel.CodeCheck.Tests;
using AElf.Kernel.SmartContract.Domain;
using AElf.Standards.ACS0;
using AElf.Types;
using Google.Protobuf;
using Shouldly;
using Xunit;

namespace AElf.Kernel.CodeCheck.Application;

public class CodeCheckValidationProviderTests: CodeCheckTestBase
{
private readonly IBlockValidationProvider _blockValidationProvider;
private readonly ICheckedCodeHashProvider _checkedCodeHashProvider;
private readonly IBlockStateSetManger _blockStateSetManger;

public CodeCheckValidationProviderTests()
{
_blockValidationProvider = GetRequiredService<IBlockValidationProvider>();
_checkedCodeHashProvider = GetRequiredService<ICheckedCodeHashProvider>();
_blockStateSetManger = GetRequiredService<IBlockStateSetManger>();
}

[Fact]
public async Task ValidateBlockAfterExecuteTest()
{
var block = new Block
{
Header = new BlockHeader
{
Height = 1,
PreviousBlockHash = HashHelper.ComputeFrom("PreviousBlockHash"),
MerkleTreeRootOfTransactions = HashHelper.ComputeFrom("MerkleTreeRootOfTransactions"),
MerkleTreeRootOfWorldState = HashHelper.ComputeFrom("MerkleTreeRootOfWorldState"),
MerkleTreeRootOfTransactionStatus = HashHelper.ComputeFrom("MerkleTreeRootOfTransactionStatus"),
Time = TimestampHelper.GetUtcNow(),
SignerPubkey = ByteString.CopyFromUtf8("SignerPubkey")
}
};
var validationResult = await _blockValidationProvider.ValidateBlockAfterExecuteAsync(block);
validationResult.ShouldBeTrue();

block.Header.Height = 2;
validationResult = await _blockValidationProvider.ValidateBlockAfterExecuteAsync(block);
validationResult.ShouldBeTrue();

var logEvent = new LogEvent
{
Name = "ContractDeployed",
Address = ZeroContractFakeAddress
};
var bloom = logEvent.GetBloom();
block.Header.Bloom = ByteString.CopyFrom(bloom.Data);
validationResult = await _blockValidationProvider.ValidateBlockAfterExecuteAsync(block);
validationResult.ShouldBeTrue();

block.Header.Height = 3;
validationResult = await _blockValidationProvider.ValidateBlockAfterExecuteAsync(block);
validationResult.ShouldBeTrue();

block.Header.Height = 4;
validationResult = await _blockValidationProvider.ValidateBlockAfterExecuteAsync(block);
validationResult.ShouldBeFalse();

await _blockStateSetManger.SetBlockStateSetAsync(new BlockStateSet
{
BlockHash = block.GetHash(),
BlockHeight = block.Height,
PreviousHash = block.Header.PreviousBlockHash
});
await _checkedCodeHashProvider.AddCodeHashAsync(new BlockIndex
{
BlockHash = block.GetHash(),
BlockHeight = block.Height
}, HashHelper.ComputeFrom(block.Height));

validationResult = await _blockValidationProvider.ValidateBlockAfterExecuteAsync(block);
validationResult.ShouldBeTrue();
}
}
52 changes: 52 additions & 0 deletions test/AElf.Kernel.CodeCheck.Tests/CodeCheckTestAElfModule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Linq;
using System.Threading.Tasks;
using AElf.Contracts.Parliament;
using AElf.Kernel.Blockchain.Application;
using AElf.Kernel.CodeCheck.Application;
using AElf.Kernel.Configuration;
using AElf.Kernel.Proposal;
using AElf.Kernel.Proposal.Infrastructure;
Expand Down Expand Up @@ -28,6 +30,7 @@ public override void ConfigureServices(ServiceConfigurationContext context)
context.Services.RemoveAll<IProposalProvider>();
context.Services.AddSingleton<IProposalProvider, ProposalProvider>();
context.Services.AddSingleton<ILogEventProcessor, CodeCheckRequiredLogEventProcessor>();
context.Services.AddSingleton<IBlockValidationProvider, CodeCheckValidationProvider>();
Configure<CodeCheckOptions>(options => { options.CodeCheckEnabled = true; });
context.Services.AddTransient(provider =>
{
Expand Down Expand Up @@ -139,6 +142,55 @@ public override void ConfigureServices(ServiceConfigurationContext context)
});
}

if (txn.MethodName == nameof(ACS0Container.ACS0Stub.GetContractCodeHashListByDeployingBlockHeight))
{
var height = Int64Value.Parser.ParseFrom(txn.Params).Value;
switch (height)
{
case 2:
return Task.FromResult(new TransactionTrace
{
ExecutionStatus = ExecutionStatus.Executed,
ReturnValue = new ContractCodeHashList().ToByteString()
});
case 3:
case 4:
return Task.FromResult(new TransactionTrace
{
ExecutionStatus = ExecutionStatus.Executed,
ReturnValue = new ContractCodeHashList{Value = { HashHelper.ComputeFrom(height) }}.ToByteString()
});
}
}

if (txn.MethodName == nameof(ACS0Container.ACS0Stub.GetSmartContractRegistrationByCodeHash))
{
var codeHash = Hash.Parser.ParseFrom(txn.Params);
if (codeHash == HashHelper.ComputeFrom(3L))
{
return Task.FromResult(new TransactionTrace
{
ExecutionStatus = ExecutionStatus.Executed,
ReturnValue = new SmartContractRegistration
{
Category = CodeCheckConstant.SuccessAudit
}.ToByteString()
});
}

if (codeHash == HashHelper.ComputeFrom(4L))
{
return Task.FromResult(new TransactionTrace
{
ExecutionStatus = ExecutionStatus.Executed,
ReturnValue = new SmartContractRegistration
{
Category = CodeCheckConstant.FailAudit
}.ToByteString()
});
}
}

return null;
});

Expand Down