Skip to content

Commit

Permalink
Merge pull request #3384 from AElfProject/fix/code-check-exception
Browse files Browse the repository at this point in the history
Fix code check failure in parallel
  • Loading branch information
jason-aelf authored Mar 29, 2023
2 parents a697652 + 36b9af5 commit a23fa62
Show file tree
Hide file tree
Showing 22 changed files with 617 additions and 351 deletions.
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,17 @@ public override Empty ChangeMethodFeeController(AuthorityInfo input)

public override MethodFees GetMethodFee(StringValue input)
{
return State.TransactionFees[input.Value];
var fees = State.TransactionFees[input.Value];
if (fees == null && input.Value == nameof(ReleaseApprovedUserSmartContract))
{
fees = new MethodFees
{
MethodName = input.Value,
IsSizeFeeFree = true
};
}

return fees;
}

public override AuthorityInfo GetMethodFeeController(Empty input)
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
14 changes: 14 additions & 0 deletions src/AElf.Kernel.CodeCheck/Application/CodeCheckJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace AElf.Kernel.CodeCheck.Application;

public class CodeCheckJob
{
public Hash BlockHash { get; set; }
public long BlockHeight { get; set; }
public byte[] ContractCode { get; set; }
public int ContractCategory { get; set; }
public bool IsSystemContract { get; set; }
public bool IsUserContract { get; set; }
public Hash CodeCheckProposalId { get; set; }
public Hash ProposedContractInputHash { get; set; }
public long BucketIndex { get; set; }
}
123 changes: 123 additions & 0 deletions src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Loader;
using System.Threading.Tasks.Dataflow;
using AElf.Kernel.Proposal.Application;

namespace AElf.Kernel.CodeCheck.Application;

public interface ICodeCheckJobProcessor
{
Task<bool> SendAsync(CodeCheckJob job);
Task CompleteAsync();
}

public class CodeCheckJobProcessor : ICodeCheckJobProcessor, ISingletonDependency
{
private readonly TransformBlock<CodeCheckJob, CodeCheckJob> _codeCheckTransformBlock;
private List<ActionBlock<CodeCheckJob>> _codeCheckProcessesJobTransformBlock;
private readonly CodeCheckOptions _codeCheckOptions;
private readonly ICheckedCodeHashProvider _checkedCodeHashProvider;
private readonly ICodeCheckService _codeCheckService;
private readonly IProposalService _proposalService;
private readonly ICodeCheckProposalService _codeCheckProposalService;

public ILogger<CodeCheckJobProcessor> Logger { get; set; }

public CodeCheckJobProcessor(IOptionsSnapshot<CodeCheckOptions> codeCheckOptions,
ICheckedCodeHashProvider checkedCodeHashProvider, IProposalService proposalService,
ICodeCheckService codeCheckService, ICodeCheckProposalService codeCheckProposalService)
{
_checkedCodeHashProvider = checkedCodeHashProvider;
_proposalService = proposalService;
_codeCheckService = codeCheckService;
_codeCheckProposalService = codeCheckProposalService;
_codeCheckOptions = codeCheckOptions.Value;
_codeCheckTransformBlock = CreateCodeCheckBufferBlock();

Logger = NullLogger<CodeCheckJobProcessor>.Instance;
}

public async Task<bool> SendAsync(CodeCheckJob job)
{
return await _codeCheckTransformBlock.SendAsync(job);
}

public async Task CompleteAsync()
{
_codeCheckTransformBlock.Complete();
await Task.WhenAll(_codeCheckProcessesJobTransformBlock.Select(o => o.Completion));
}

private TransformBlock<CodeCheckJob, CodeCheckJob> CreateCodeCheckBufferBlock()
{
var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };

var updateBucketIndexTransformBlock = new TransformBlock<CodeCheckJob, CodeCheckJob>(UpdateBucketIndex,
new ExecutionDataflowBlockOptions
{
BoundedCapacity = Math.Max(_codeCheckOptions.MaxBoundedCapacity, 1),
MaxDegreeOfParallelism = _codeCheckOptions.MaxDegreeOfParallelism
});

_codeCheckProcessesJobTransformBlock = new List<ActionBlock<CodeCheckJob>>();
for (var i = 0; i < _codeCheckOptions.MaxDegreeOfParallelism; i++)
{
var processCodeCheckJobTransformBlock = new ActionBlock<CodeCheckJob>(
async codeCheckJob => await ProcessCodeCheckJobAsync(codeCheckJob),
new ExecutionDataflowBlockOptions
{
BoundedCapacity = Math.Max(_codeCheckOptions.MaxBoundedCapacity, 1),
EnsureOrdered = false
});
var index = i;
updateBucketIndexTransformBlock.LinkTo(processCodeCheckJobTransformBlock, linkOptions,
codeCheckJob => codeCheckJob.BucketIndex == index);
_codeCheckProcessesJobTransformBlock.Add(processCodeCheckJobTransformBlock);
}

return updateBucketIndexTransformBlock;
}

private async Task ProcessCodeCheckJobAsync(CodeCheckJob job)
{
var codeCheckResult = await _codeCheckService.PerformCodeCheckAsync(job.ContractCode, job.BlockHash,
job.BlockHeight, job.ContractCategory, job.IsSystemContract, job.IsUserContract);

var codeHash = HashHelper.ComputeFrom(job.ContractCode);
Logger.LogInformation("Code check result: {codeCheckResult}, code hash: {codeHash}", codeCheckResult,
codeHash.ToHex());

if (!codeCheckResult)
return;

if (job.IsUserContract)
{
_codeCheckProposalService.AddReleasableProposal(job.CodeCheckProposalId, job.ProposedContractInputHash,
job.BlockHeight);
}

// Cache proposal id to generate system approval transaction later
_proposalService.AddNotApprovedProposal(job.CodeCheckProposalId, job.BlockHeight);

await _checkedCodeHashProvider.AddCodeHashAsync(new BlockIndex
{
BlockHash = job.BlockHash,
BlockHeight = job.BlockHeight
}, codeHash);
}

private CodeCheckJob UpdateBucketIndex(CodeCheckJob job)
{
var assemblyLoadContext = new AssemblyLoadContext(null, true);
var assembly = assemblyLoadContext.LoadFromStream(new MemoryStream(job.ContractCode));

job.BucketIndex =
Math.Abs(HashHelper.ComputeFrom(assembly.GetName().Name).ToInt64() % _codeCheckOptions.MaxDegreeOfParallelism);
assemblyLoadContext.Unload();

return job;
}
}

This file was deleted.

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")]
4 changes: 3 additions & 1 deletion src/AElf.Kernel.CodeCheck/CodeCheckAElfModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ public class CodeCheckAElfModule : AElfModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<CodeCheckOptions>(configuration.GetSection("CodeCheck"));

context.Services
.AddSingleton<IBlocksExecutionSucceededLogEventProcessor, CodeCheckRequiredLogEventProcessor>();
//context.Services.AddSingleton<IBlockAcceptedLogEventProcessor, ContractDeployedLogEventProcessor>();
context.Services.AddSingleton<IContractAuditorContainer, ContractAuditorContainer>();
context.Services.AddSingleton<IBlockValidationProvider, CodeCheckValidationProvider>();
context.Services.AddTransient<ITransactionValidationProvider, CodeCheckTransactionValidationProvider>();
context.Services.AddTransient<ISystemTransactionGenerator, CodeCheckProposalReleaseTransactionGenerator>();
}
}
Loading

0 comments on commit a23fa62

Please sign in to comment.