private async Task <RegisteredDebot> FetchDebotAsync(BrowserData state, string addr) { var handle = await _debot.Client.Debot.InitAsync(new ParamsOfInit { Address = addr }, GetExecuteCallback(state)); using var bots = await state.Bots.LockAsync(); bots.Instance.Add(addr, new RegisteredDebot { DebotAbi = handle.DebotAbi, DebotHandle = handle.DebotHandle, Info = handle.Info }); return(handle); }
public async Task ExecuteAsync(List <DebotStep> steps, List <string> terminalOutputs = null) { var state = new BrowserData { Current = new Mutex <CurrentStepData>(new CurrentStepData()), Next = new Mutex <List <DebotStep> >(steps), Keys = _debot.Keys, Address = _debot.Address, Finished = false, SigningBoxInput = new SigningBoxInput(await _debot.Client.Crypto.GetSigningBoxAsync(_debot.Keys)), Info = new DebotInfo { Dabi = ((Abi.Contract)_debot.Abi).Value.ToJson().ToString() }, Terminal = new Mutex <Terminal>(new Terminal(terminalOutputs ?? new List <string>())) }; await ExecuteFromStateAsync(state, true); }
public async Task ExecuteWithDetailsAsync( List <DebotStep> steps, DebotInfo debotInfo, List <ExpectedTransaction> activity, List <string> terminalOutputs = null) { var state = new BrowserData { Current = new Mutex <CurrentStepData>(new CurrentStepData()), Next = new Mutex <List <DebotStep> >(steps), Keys = _debot.Keys, Address = _debot.Address, Finished = false, Info = debotInfo, SigningBoxInput = new SigningBoxInput(await _debot.Client.Crypto.GetSigningBoxAsync(_debot.Keys)), Activity = new Mutex <List <ExpectedTransaction> >(activity), Terminal = new Mutex <Terminal>(new Terminal(terminalOutputs ?? new List <string>())) }; await ExecuteFromStateAsync(state, true); }
private Func <ParamsOfAppDebotBrowser, Task <ResultOfAppDebotBrowser> > GetExecuteCallback(BrowserData state) { return(async @params => { if (@params == null) { throw new ArgumentNullException(nameof(@params)); } switch (@params) { case ParamsOfAppDebotBrowser.Log log: using (var step = await state.Current.LockAsync()) { step.Instance.Outputs.Add(log.Msg); } return null; case ParamsOfAppDebotBrowser.Switch @switch: using (var switchStarted = await state.SwitchStarted.LockAsync()) { Assert.False(switchStarted.Swap(true)); } if (@switch.ContextId == 255) // STATE_EXIT { state.Finished = true; } using (var step = await state.Current.LockAsync()) { step.Instance.AvailableActions.Clear(); } return null; case ParamsOfAppDebotBrowser.ShowAction action: using (var step = await state.Current.LockAsync()) { step.Instance.AvailableActions.Add(action.Action); } return null; case ParamsOfAppDebotBrowser.Send message: using (var queue = await state.MsgQueue.LockAsync()) { queue.Instance.Enqueue(message.Message); } return null; case ParamsOfAppDebotBrowser.SwitchCompleted: using (var switchStarted = await state.SwitchStarted.LockAsync()) { Assert.True(switchStarted.Swap(false)); } return null; case ParamsOfAppDebotBrowser.Input: using (var step = await state.Current.LockAsync()) { var value = step.Instance.Step.Inputs.RemoveFirst(); return new ResultOfAppDebotBrowser.Input { Value = value }; } case ParamsOfAppDebotBrowser.GetSigningBox: var signingBox = await _debot.Client.Crypto.GetSigningBoxAsync(state.Keys); return new ResultOfAppDebotBrowser.GetSigningBox { SigningBox = signingBox.Handle }; case ParamsOfAppDebotBrowser.InvokeDebot invoke: List <DebotStep> steps; using (var step = await state.Current.LockAsync()) { steps = step.Instance.Step.Invokes.RemoveFirst(); } steps[0].Choice = 1; var current = new CurrentStepData { AvailableActions = new List <DebotAction> { invoke.Action } }; var newState = new BrowserData { Current = new Mutex <CurrentStepData>(current), Next = new Mutex <List <DebotStep> >(steps), Keys = state.Keys, Address = invoke.DebotAddr, Finished = false, SigningBoxInput = new SigningBoxInput(await _debot.Client.Crypto.GetSigningBoxAsync(_debot.Keys)) }; await ExecuteFromStateAsync(newState, false); return new ResultOfAppDebotBrowser.InvokeDebot(); case ParamsOfAppDebotBrowser.Approve approve: var approved = false; using (var activityLock = await state.Activity.LockAsync()) { var expected = activityLock.Instance.RemoveFirst(); if (expected != null) { approved = expected.Approved; var activity = approve.Activity as DebotActivity.Transaction; Assert.NotNull(activity); Assert.Equal(expected.Dst, activity.Dst); Assert.Equal(expected.Out, activity.Out); Assert.Equal(expected.Setcode, activity.Setcode); Assert.Equal(expected.Signkey, activity.Signkey); Assert.NotEqual(0u, activity.SigningBoxHandle); Assert.True(activity.Fee > BigInteger.Zero); } } return new ResultOfAppDebotBrowser.Approve { Approved = approved }; default: throw new NotSupportedException($"Callback parameter type not supported: {@params.GetType()}"); } }); }
private async Task <(uint, JToken)> CallTerminalAsync(BrowserData data, string func, JToken args) { using var terminal = await data.Terminal.LockAsync(); return(terminal.Instance.Call(func, args)); }
private async Task HandleMessageQueueAsync(RegisteredDebot debot, BrowserData data) { var msg = await data.PopMessageAsync(); while (msg != null) { var parsed = await _debot.Client.Boc.ParseMessageAsync(new ParamsOfParse { Boc = msg }); var body = parsed.Parsed["body"]?.ToString(); Assert.NotNull(body); var destAddr = parsed.Parsed["dst"]?.ToString(); Assert.NotNull(destAddr); var srcAddr = parsed.Parsed["src"]?.ToString(); var wcAndAddr = destAddr.Split(":"); Assert.True(wcAndAddr.Length > 1); var wc = int.Parse(wcAndAddr[0]); var interfaceId = wcAndAddr[1]; if (wc == DebotWc) { Assert.Contains(interfaceId, _supportedInterfaces); var decoded = await _debot.Client.Abi.DecodeMessageBodyAsync(new ParamsOfDecodeMessageBody { Abi = GetInterfaceAbi(interfaceId), Body = body, IsInternal = true }); _logger.Information($"call for interface id {interfaceId}"); _logger.Information($"request: {decoded.Name} ({decoded.Value})"); var(funcId, returnArgs) = _supportedInterfaces[0] == interfaceId ? data.Echo.Call(decoded.Name, decoded.Value) : _supportedInterfaces[1] == interfaceId ? await CallTerminalAsync(data, decoded.Name, decoded.Value) : data.SigningBoxInput.Call(decoded.Name, decoded.Value); _logger.Information($"response: {funcId} ({returnArgs})"); var srcDebot = await data.GetDebotAsync(srcAddr); var message = await _debot.Client.Abi.EncodeInternalMessageAsync(new ParamsOfEncodeInternalMessage { Abi = new Abi.Json { Value = srcDebot.DebotAbi }, Address = srcAddr, CallSet = funcId == 0 ? null : new CallSet { FunctionName = $"0x{funcId:X}", Input = returnArgs }, Value = "1000000000000000" }); await _debot.Client.Debot.SendAsync(new ParamsOfSend { DebotHandle = debot.DebotHandle, Message = message.Message }); } else { if (!await data.DebotFetchedAsync(destAddr)) { await FetchDebotAsync(data, destAddr); } var debotHandle = (await data.GetDebotAsync(destAddr)).DebotHandle; await _debot.Client.Debot.SendAsync(new ParamsOfSend { DebotHandle = debotHandle, Message = msg }); } msg = await data.PopMessageAsync(); } }
private async Task ExecuteFromStateAsync(BrowserData state, bool callStart) { if (callStart) { var res = await _debot.Client.Debot.FetchAsync(new ParamsOfFetch { Address = state.Address }); var serializer = new TonSerializer(); var expectedAbi = serializer.Deserialize <JToken>(state.Info.Dabi); var actualAbi = serializer.Deserialize <JToken>(res.Info.Dabi); Assert.Equal(expectedAbi, actualAbi); if (state.Info != null) { Assert.NotNull(res.Info); Assert.Equal(state.Info.Author, res.Info.Author); Assert.Equal(state.Info.Dabi, res.Info.Dabi); Assert.Equal(state.Info.Hello, res.Info.Hello); Assert.Equal(state.Info.Icon, res.Info.Icon); Assert.Equal(state.Info.Interfaces ?? new string[0], res.Info.Interfaces); Assert.Equal(state.Info.Caption, res.Info.Caption); Assert.Equal(state.Info.Language, res.Info.Language); Assert.Equal(state.Info.Name, res.Info.Name); Assert.Equal(state.Info.Publisher, res.Info.Publisher); } else { Assert.Null(res.Info); } } var handle = await FetchDebotAsync(state, state.Address); if (callStart) { await _debot.Client.Debot.StartAsync(new ParamsOfStart { DebotHandle = handle.DebotHandle }); } while (!state.Finished) { await HandleMessageQueueAsync(handle, state); DebotAction action; using (var step = await state.Current.LockAsync()) { if (!step.Instance.AvailableActions.Any()) { break; } using (var next = await state.Next.LockAsync()) { step.Instance.Step = next.Instance.RemoveFirst(); } step.Instance.Outputs.Clear(); action = step.Instance.AvailableActions[step.Instance.Step.Choice - 1]; } _logger.Information($"Executing action {action.Name}"); await _debot.Client.Debot.ExecuteAsync(new ParamsOfExecute { Action = action, DebotHandle = handle.DebotHandle }); using (var step = await state.Current.LockAsync()) { var leftArr = step.Instance.Step.Outputs; var rightArr = step.Instance.Outputs; Assert.Equal(leftArr.Count, rightArr.Count); for (var i = 0; i < leftArr.Count; ++i) { var left = leftArr[i]; var right = rightArr[i]; Assert.NotNull(left); Assert.NotNull(right); var pos = left.IndexOf("{}", StringComparison.Ordinal); if (pos >= 0) { Assert.Equal(left.Substring(0, pos), right.Substring(0, pos)); } else { Assert.Equal(left, right); } } Assert.Empty(step.Instance.Step.Inputs); Assert.Empty(step.Instance.Step.Invokes); if (!step.Instance.AvailableActions.Any()) { break; } } } using (var next = await state.Next.LockAsync()) { Assert.Empty(next.Instance); } using var terminal = await state.Terminal.LockAsync(); Assert.Empty(terminal.Instance.Messages); }