private void ExecuteCycle(int n, int f) { var stateManager = _container?.Resolve <IStateManager>(); var contractRegisterer = _container?.Resolve <IContractRegisterer>(); var tx = new TransactionReceipt(); var sender = new BigInteger(0).ToUInt160(); var context = new InvocationContext(sender, stateManager !.LastApprovedSnapshot, tx); var contract = new GovernanceContract(context); var ecdsaKeys = Enumerable.Range(0, n) .Select(_ => Crypto.GeneratePrivateKey()) .Select(x => x.ToPrivateKey()) .Select(x => new EcdsaKeyPair(x)) .ToArray(); var pubKeys = ecdsaKeys.Select(x => CryptoUtils.EncodeCompressed(x.PublicKey)).ToArray(); var keyGens = Enumerable.Range(0, n) .Select(i => new TrustlessKeygen(ecdsaKeys[i], ecdsaKeys.Select(x => x.PublicKey), f, 0)) .ToArray(); var cycle = 0.ToUInt256(); var messageLedger = new RandomSamplingQueue <QueueItem>(); messageLedger.Enqueue(new QueueItem(-1, null)); // call ChangeValidators method { byte[][] validators = pubKeys; var input = ContractEncoder.Encode(GovernanceInterface.MethodChangeValidators, cycle, validators); var call = contractRegisterer !.DecodeContract(context, ContractRegisterer.GovernanceContract, input); Assert.IsNotNull(call); var frame = new SystemContractExecutionFrame(call !, context, input, 100_000_000); Assert.AreEqual(ExecutionStatus.Ok, contract.ChangeValidators(cycle, validators, frame)); } // check correct validator { for (var i = 0; i < n; i++) { var input = ContractEncoder.Encode(GovernanceInterface.MethodIsNextValidator, pubKeys[i]); var call = contractRegisterer.DecodeContract(context, ContractRegisterer.GovernanceContract, input); Assert.IsNotNull(call); var frame = new SystemContractExecutionFrame(call !, context, input, 100_000_000); Assert.AreEqual(ExecutionStatus.Ok, contract.IsNextValidator(pubKeys[i], frame)); Assert.AreEqual(frame.ReturnValue, 1.ToUInt256().ToBytes()); } } // check incorrect validator { byte[] incorrectPubKey = pubKeys[0].Reverse().ToArray(); var input = ContractEncoder.Encode(GovernanceInterface.MethodIsNextValidator, incorrectPubKey); var call = contractRegisterer.DecodeContract(context, ContractRegisterer.GovernanceContract, input); Assert.IsNotNull(call); var frame = new SystemContractExecutionFrame(call !, context, input, 100_000_000); Assert.AreEqual(ExecutionStatus.Ok, contract.IsNextValidator(incorrectPubKey, frame)); Assert.AreEqual(frame.ReturnValue, 0.ToUInt256().ToBytes()); } while (messageLedger.Count > 0) { QueueItem?msg; var success = messageLedger.TryDequeue(out msg); Assert.IsTrue(success); switch (msg.payload) { case null: for (var i = 0; i < n; ++i) { messageLedger.Enqueue(new QueueItem(i, keyGens[i].StartKeygen())); } break; case CommitMessage commitMessage: for (var i = 0; i < n; ++i) { if (i == 0) { byte[] commitment = commitMessage.Commitment.ToBytes(); byte[][] encryptedRows = commitMessage.EncryptedRows; var input = ContractEncoder.Encode(GovernanceInterface.MethodKeygenCommit, cycle, commitment, encryptedRows); var call = contractRegisterer.DecodeContract(context, ContractRegisterer.GovernanceContract, input); Assert.IsNotNull(call); var frame = new SystemContractExecutionFrame(call !, context, input, 100_000_000); Assert.AreEqual(ExecutionStatus.Ok, contract.KeyGenCommit(cycle, commitment, encryptedRows, frame)); // several calls is ok Assert.AreEqual(ExecutionStatus.Ok, contract.KeyGenCommit(cycle, commitment, encryptedRows, frame)); } messageLedger.Enqueue(new QueueItem(i, keyGens[i].HandleCommit(msg.sender, commitMessage))); } break; case ValueMessage valueMessage: for (var i = 0; i < n; ++i) { if (i == 0) { var proposer = new BigInteger(msg.sender).ToUInt256(); var input = ContractEncoder.Encode(GovernanceInterface.MethodKeygenSendValue, cycle, proposer, valueMessage.EncryptedValues); var call = contractRegisterer.DecodeContract(context, ContractRegisterer.GovernanceContract, input); Assert.IsNotNull(call); var frame = new SystemContractExecutionFrame(call !, context, input, 100_000_000); Assert.AreEqual(ExecutionStatus.Ok, contract.KeyGenSendValue(cycle, proposer, valueMessage.EncryptedValues, frame)); } keyGens[i].HandleSendValue(msg.sender, valueMessage); } break; default: Assert.Fail($"Message of type {msg.GetType()} occurred"); break; } } Assert.IsTrue(keyGens.All((x) => x.Finished())); // finish cycle { var input = ContractEncoder.Encode(GovernanceInterface.MethodFinishCycle, cycle); var call = contractRegisterer.DecodeContract(context, ContractRegisterer.GovernanceContract, input); Assert.IsNotNull(call); var frame = new SystemContractExecutionFrame(call !, context, input, 100_000_000); // set next cycle block number in frame: frame.InvocationContext.Receipt.Block = StakingContract.CycleDuration; Assert.AreEqual(ExecutionStatus.Ok, contract.FinishCycle(cycle, frame)); } }
private static ThresholdKeyring[] SimulateKeygen(int n, int f, DeliveryServiceMode mode) { var ecdsaKeys = Enumerable.Range(0, n) .Select(_ => Crypto.GeneratePrivateKey()) .Select(x => x.ToPrivateKey()) .Select(x => new EcdsaKeyPair(x)) .ToArray(); var keyGens = Enumerable.Range(0, n) .Select(i => new TrustlessKeygen(ecdsaKeys[i], ecdsaKeys.Select(x => x.PublicKey), f, 0)) .ToArray(); var messageLedger = new RandomSamplingQueue <QueueItem>(); messageLedger.Enqueue(new QueueItem(-1, null)); var curKeys = keyGens.Select(_ => (ThresholdKeyring?)null).ToArray(); while (messageLedger.Count > 0) { QueueItem?msg; var success = mode switch { DeliveryServiceMode.TAKE_FIRST => messageLedger.TryDequeue(out msg), DeliveryServiceMode.TAKE_LAST => messageLedger.TryTakeLast(out msg), DeliveryServiceMode.TAKE_RANDOM => messageLedger.TrySample(out msg), _ => throw new NotImplementedException($"Unknown mode {mode}") }; Assert.IsTrue(success); switch (msg?.payload) { case null: for (var i = 0; i < n; ++i) { messageLedger.Enqueue(new QueueItem(i, keyGens[i].StartKeygen())); } break; case CommitMessage commitMessage: for (var i = 0; i < n; ++i) { messageLedger.Enqueue(new QueueItem(i, keyGens[i].HandleCommit(msg.sender, commitMessage))); } break; case ValueMessage valueMessage: for (var i = 0; i < n; ++i) { keyGens[i].HandleSendValue(msg.sender, valueMessage); } break; default: Assert.Fail($"Message of type {msg.GetType()} occurred"); break; } for (var i = 0; i < n; ++i) { var curKey = keyGens[i].TryGetKeys(); if (curKey is null) { Assert.AreEqual(null, curKeys[i]); continue; } if (!curKeys[i].HasValue) { curKeys[i] = curKey; continue; } Assert.IsTrue(curKey.Value.TpkePrivateKey.ToBytes() .SequenceEqual(curKeys[i] !.Value.TpkePrivateKey.ToBytes())); Assert.AreEqual(curKey.Value.PublicPartHash(), curKeys[i] !.Value.PublicPartHash()); } for (var i = 0; i < n; ++i) { keyGens[i] = TestSerializationRoundTrip(keyGens[i], ecdsaKeys[i]); } } for (var i = 0; i < n; ++i) { Assert.IsTrue(keyGens[i].Finished()); Assert.AreNotEqual(curKeys[i], null); } var keys = keyGens .Select(x => x.TryGetKeys() ?? throw new Exception()) .ToArray(); for (var i = 0; i < n; ++i) { Assert.AreEqual(keys[0].TpkePublicKey, keys[i].TpkePublicKey); Assert.AreEqual(keys[0].ThresholdSignaturePublicKeySet, keys[i].ThresholdSignaturePublicKeySet); } return(keys); }