In this case, if there was an opportunity to use await, then there would be no reason to create a question, but await is not recommended to be used in contracts:
Note: This feature was designed to be used in debots, in usual contracts use it at your own risk. https://github.com/tonlabs/TON-Solidity-Compiler/blob/master/API.md#synchronous-calls
Pong.sol
pragma ton-solidity ^0.51.0;
contract Pong {
function get(uint b) external responsible returns (uint) {
return b + 1;
}
}
Ping.sol
pragma ton-solidity ^0.51.0;
interface IPong {
function get(uint b) external responsible returns (uint);
}
contract Ping {
uint public result;
uint public tmp;
function run(address pong, uint a) public view returns(uint) {
update(pong, a);
tvm.accept();
return a + tmp;
}
function update(address pong, uint a) internal pure {
IPong(pong).get{callback: Ping.onGet}(a);
}
function onGet(uint b) external {
tvm.accept();
tmp = b;
}
}
Run.bash
#!/usr/bin/env bash
set -o errexit
tondev se reset
rm -fr *.abi.json *.tvc
# Deploy Pong Contract
tondev sol compile Pong.sol
tondev contract deploy Pong --value 1000000000
pongAddress=$(tondev contract info Pong | grep Address | cut -d':' -f3 | cut -d' ' -f1)
echo "$pongAddress"
# Deploy Ping Contract
tondev sol compile Ping.sol
tondev contract deploy Ping --value 1000000000
pingAddress=$(tondev contract info Ping | grep Address | cut -d':' -f3 | cut -d' ' -f1)
echo "$pingAddress"
# Run
tondev contract run Ping run --input "pong:$pongAddress,a:1" | grep value0
# value0:0x0000000000000000000000000000000000000000000000000000000000000001
tondev contract run Ping run --input "pong:$pongAddress,a:1" | grep value0
# value0:0x0000000000000000000000000000000000000000000000000000000000000003
The purpose of the question is to understand how to get the value 3 on the first request, if possible. If not, then a more extensive explanation of how to develop contracts in the conditions of an asynchronous blockchain.
You can do this in asynchronous style more simply (without callback and await).
I changed the code of your contracts:
pragma ton-solidity ^0.51.0;
interface IPing {
function onGet(uint b) external;
}
contract Pong {
function get(uint b) external {
IPing(msg.sender).onGet(b + 1);
}
}
pragma ton-solidity ^0.51.0;
interface IPong {
function get(uint b) external;
}
contract Ping {
uint public tmp;
function run(address pong, uint a) public {
IPong(pong).get(a);
tvm.accept();
tmp = a + tmp;
}
function onGet(uint b) external {
tvm.accept();
tmp = b + tmp;
}
}
Now call a little changed this Run.bash
#!/usr/bin/env bash
set -o errexit
tondev se reset
tondev network default se
rm -fr *.abi.json *.tvc
# Deploy Pong Contract
tondev sol compile Pong.sol
tondev contract deploy Pong --value 1000000000
pongAddress=$(tondev contract info Pong | grep Address | cut -d':' -f3 | cut -d' ' -f1)
echo "$pongAddress"
# Deploy Ping Contract
tondev sol compile Ping.sol
tondev contract deploy Ping --value 1000000000
pingAddress=$(tondev contract info Ping | grep Address | cut -d':' -f3 | cut -d' ' -f1)
echo "$pingAddress"
# Run
tondev contract run Ping run --input "pong:$pongAddress,a:1" &> /dev/null
tondev contract run-local Ping tmp | grep tmp
# tmp:0x0000000000000000000000000000000000000000000000000000000000000003
tondev contract run Ping run --input "pong:$pongAddress,a:1" &> /dev/null
tondev contract run-local Ping tmp | grep tmp
# tmp:0x0000000000000000000000000000000000000000000000000000000000000006
A bit of my philosophy, why it is better:
the contracts must works without waiting. So callbacks and awaits, it is not good.
Callbacks are very useful in debots, and it can take place there because debots running on your device. But it is another story…
Related
need help to get the correct funcID. I've Tried many ways. Will be glad to get your help.
Thanks!
slugSha256() {
slug=$(echo -n "$1" | sha256sum | cut -d' ' -f1)
echo -n $((16#"${slug:0:8}"))
}
functionHash() {
hash=$(slugSha256 "$(echo -n "$1" | sed 's/ //g')")
if [[ "$1" == *constructor* ]]; then hash=$((hash ^ (2 ** 31))); fi
printf '0x%x' "$hash"
}
functionID() { functionHash "$1v2"; }
`
functionID "main()(string)" # 0xb0992770
functionID "redeem(uint256, string, uint256)()" # 0x58160fa0
functionID "constructor()()" # 0x68b55f3f
foo.sol
pragma ever-solidity >= 0.64.0;
contract foo {
constructor() public {}
function main() public returns (string) {}
function redeem(uint256 a, string b, uint256 c) public {}
}
via everdev npx everdev sol update
~/.everdev/solidity/solc --function-ids foo.sol
via sold:
sold --function-ids foo.sol
out:
{
"constructor": "0x68b55f3f",
"main": "0x30992770",
"redeem": "0x58160fa0"
}
via web tool https://ever.bytie.moe/serializer
For the constructor, you need to clear the highest bit to get the correct funcID
For example, I have a function
function addClaimers(ClaimItemImport[] claimItems) onlyOwner external
and the ClaimItemImport struct looks as follows:
struct ClaimItemImport {
uint128 balance;
uint256 claimAddress;
}
How to call it from tondev with parameters?
This does not work:
tondev contract run Contract.abi.json --address XYZ addClaimers -i claimItems:[[123, 0x123], [456, 0x456]]
--address 0:540c1837656674d548c934258ddec9b5fd11b543da977b0016c14b5650bc7eb5 \
--input '{ "point": { "color": "red", "center": { "x": 1, "y": 2 } } }'
Check readme in the end of this section https://github.com/tonlabs/tondev#run-contract-deployed-on-the-network
I've updated my tondev + used the following syntax:
tondev contract run FidosafeDAO.abi.json addClaimers --address 0:2540afc97408aec8e094eaf2695acd8fd4c301830590214be584e4b627e5bf90 -i '{"claimItems":[{"balance":0,"claimAddress":0}]}'
I'm having a problem with the command send in expect script.
I've never used the expect before, so there are so many peculiarity about it that I don't know, mainly about syntax.
Actually it catches the cpu usage btw.
top -n 10 -d 0.01 | awk 'BEGIN{FS="[,%]"; printf "(" }/^Cpu/{
gsub(/[^0-9.,]+/,"",$7); gsub(/^3949/,"",$7); printf $7" + "}
END{print 0") / 10"}' | bc
What I would like to do is: with expect, using the command send. I want pass that string by the script.
I'm trying on this way:
#!/bin/expect
set timeout 20
set user [lindex $argv 0]
set password [lindex $argv 1]
set host [lindex $argv 2]
set prompt "$ "
;
proc gestat { } {
;
send -- "echo -n 'MEMORY_FREE: ' && free -t | grep 'buffers/cache' | awk '{print \$4/(\$3+\$4) * 100}'\r"
send -- "top -n 10 -d 0.01 | awk \'BEGIN{FS='[,%]'; printf '(' }/^Cpu/{ gsub(/[^0-9.,]+/,'',\$7); gsub(/^3949/,'',\$7); printf \$7' + '} END{print 0') / 10'}\' | bc\r"
return
}
;
spawn ssh $user#$host
while (1) {
expect {
"(yes/no)? " {
send -- "yes\r"
}
"password: " {
send -- "$password\r"
}
"$prompt" {
gestat
break
}
}
}
expect "$prompt"
send -- "exit\r"
expect eof
But the send does not work. The error is:
invalid command name ",%"
while executing ",%"
(procedure "gestat" line 4)
I guess it would be because the expect "parser". But I really don't know.
Sorry for my poor english.
Since expect is an extension of Tcl, the [] characters are special in double quoted strings. You'll need to protect them.
Additionally, you'll get in trouble in the second call to awk -- you cannot embed single quotes in a single quoted string.
You need to use Tcl's {} quoting -- braces in Tcl quote strings like the shell uses single quotes:
send -- {echo -n 'MEMORY_FREE: ' && free -t | grep 'buffers/cache' | awk '{print $4/($3+$4) * 100}'}
send -- \r
send -- {top -n 10 -d 0.01 | awk 'BEGIN{FS="[,%]"; printf "(" }/^Cpu/{ gsub(/[^0-9.,]+/,"",$7); gsub(/^3949/,"",$7); printf $7 " + "} END{print 0 ") / 10"}' | bc}
send -- \r
In this README I give instructions for a quick CL for testing the released tool. I think it would be much better if I provided a .bat and unix script which executed the commands in one click/command. At the same time, unlike a compiled program, it's transparent and users can open the script with the editor and inspect the commands executed.
Can I in a bat save a file?
This is what I'd like it to execute.
$ vim Test.java (windows: notepad Test.java)
class T {
private static void p(int i, Double d, String... s){}
}
public class Test{
#com.dp4j.InjectReflection public void t() {
T.p(1,new Double(2),"hello", "reflection");
}
}
$ ls Test.class T.class (windows: dir Test.class T.class)
ls: Test.class: No such file or directory ls: T.class: No such file or directory
$ javac -cp dp4j-1.0-jar-with-dependencies.jar Test.java
$ ls Test.class T.class (windows: dir Test.class T.class)
ls Test.class T.class
Yes.
You can put each line in an echo command piped to the file:
echo class T { > MyFile.java
echo private static void p(int i, Double d, String... s){} >> MyFile.java
echo } >> MyFile.java
echo ... >> MyFile.java
> creates a file; >> appends to it.
You can then compile it normally.
Um, perhaps this?
// 2>NUL&GOTO :START
//Just ignore the above line, it's for the batch script.
class T {
private static void p(int i, Double d, String... s){}
}
public class Test{
#com.dp4j.InjectReflection public void t() {
T.p(1,new Double(2),"hello", "reflection");
}
}
/*We start the batch script here.
:START
#CLS&ECHO OFF
START NOTEPAD %0
IF EXIST Test.class GOTO :EXISTS
IF EXIST T.class GOTO :EXISTS
JAVAC -cp dp4j-1.0-jar-with-dependencies.jar %0
GOTO :END
:EXISTS
ECHO There is a preexisting class file. Aborting.
:END
REM We end the batch script here.*/
By the way, here the batch script and java source are the same file.
This is a unix exectable script that does what I want. I don't know if it works with cygwin on windows:
#!/bin/sh
v=1.1
test_file="Test10.java"
jar_file="dp4j-$v-jar-with-dependencies.jar"
cmd="curl -O --fail -L http://downloads.sourceforge.net/project/dp4j/$v/$jar_file"
echo $cmd
$cmd
echo
# Start
cat > $test_file << __EOF__
class T10 {
private static void p(int i, Double d, String... s){}
}
public class Test10{
#com.dp4j.InjectReflection
public void t() {
T10.p(1,new Double(2),"hello", "reflection");
}
}
__EOF__
cmd="cat $test_file"
echo $cmd
$cmd
echo
cmd="javac -Averbose=true -cp $jar_file $test_file"
echo $cmd
$cmd
echo
echo "TEST PASSED: $test_file was compiled with Reflection Injected."
echo "When JUnit/TestNG.jar is in the classpath you may use #Test in lieu of #InjectReflection."
echo "Javadoc, sources, and other artifacts maybe downloaded from http://repo2.maven.org/maven2/com/dp4j/dp4j/"$v"/"
Here's one line command to download it and execute it:
wget http://sourceforge.net/projects/dp4j/files/1.2/TESTDRIVE ; chmod +x TESTDRIVE ; ./TESTDRIVE
I have now whittled this down to a minimal test case. Thus far I have been able to determine that this is an issue related to pseudo-terminals which come about with the pipe of ssh. Adding the '-t -t' to the ssh call improved things, in that now, it takes a second call to fgets() to cause the issue. I suspect that the stderr output of the ssh command somehow works into the issue, for now I have redirected stderr to stdout in the ssh code to execute. I do wonder if the "tcgetattr: Invalid argument" error is part of the problem, but am not sure how to get rid of that. It seems to come from the -t -t being present. I believe the -t -t is moving in the right direction, but I have to set up the pseudo terminal for stderr somehow and perhaps the test will work properly?
The Makefile:
test:
gcc -g -DBUILD_MACHINE='"$(shell hostname)"' -c -o test.o test.c
gcc -g -o test test.o
.PHONY: clean
clean:
rm -rf test.o test
The test.c source file:
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
const unsigned int bufSize = 32;
char buf1[bufSize];
char buf2[bufSize];
int ssh = argv[1][0] == 'y';
const char *cmd = ssh ? "ssh -t -t " BUILD_MACHINE " \"ls\" 2>&1" : "ls";
FILE *fPtr = popen(cmd, "r");
if (fPtr == NULL) {
fprintf(stderr,"Unable to spawn command.\n");
perror("popen(3)");
exit(1);
}
printf("Command: %s\n", cmd);
if (feof(fPtr) == 0 && fgets(buf2, bufSize, fPtr) != NULL) {
printf("First result: %s\n", buf2);
if (feof(fPtr) == 0 && fgets(buf2, bufSize, fPtr) != NULL) {
printf("Second result: %s\n", buf2);
int nRead = read(fileno(stdin), buf1, bufSize);
if (nRead == 0) {
printf("???? popen() of ssh consumed the beginning of stdin ????\n");
} else if (nRead > 0) {
if (strncmp("The quick brown fox jumped", buf1, 26) != 0) {
printf("??? Failed ???\n");
} else {
printf("!!!!!!! Without ssh popen() did not consume stdin !!!!!!!\n");
}
}
}
}
}
This shows it running the passing way:
> echo "The quick brown fox jumped" | ./test n
Command: ls
First result: ARCH.linux_26_i86
Second result: Makefile
!!!!!!! Without ssh popen() did not consume stdin !!!!!!!
This shows it running the failing way:
> echo "The quick brown fox jumped" | ./test y
Command: ssh -t -t hostname "ls" 2>&1
First result: tcgetattr: Invalid argument
Second result: %backup%~ gmon.out
???? popen() of ssh consumed the beginning of stdin ????
Okay, I have got this working finally. The secret was to supply /dev/null as the input to my ssh command as follows from the test case above:
const char *cmd
= ssh ? "ssh -t -t " BUILD_MACHINE " \"ls\" 2>&1 < /dev/null" : "ls";
However, while the code works correctly, I get a nasty message which apparently I can ignore for my purposes (although I'd like to make the message go away):
tcgetattr: Inappropriate ioctl for device