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
Merged
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