コード例 #1
0
        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);
        }
コード例 #2
0
        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);
        }
コード例 #3
0
        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);
        }
コード例 #4
0
        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()}");
                }
            });
        }
コード例 #5
0
        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));
        }
コード例 #6
0
        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();
            }
        }
コード例 #7
0
        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);
        }