您正在查看: EOS 分类下的文章

在windows上安装运行EOS

原文地址:https://steemit.com/eos/@tokenika/installing-and-running-eos-on-windows

如果你是一名windows c++开发者,你最喜欢的ide是ms visual studio,那么你可能就想知道能不能直接在windows上编译eos代码。

我们在windows c++编译器上做了几次试验,结果不行。虽然eos的代码有win32的标签,但主要的问题在于,它前后不一致。而且,windows的clang编译器和unix的clang编译器有些不同,它的限制更多。比如,像std::move(u8"env")这样的表达式在windows下是无效的。

不过,我们找到了一个替代的解决办法: Windows Subsystem for Linux,它捆绑了 Visual Studio Code ide。待会你会看到,用这个方法比直接在windows下编译代码更好。

准备工具

你需要 Windows 10, 版本1703, 也就是 Creators Update。想验证你的windows版本,你可以打开设置面板,然后进入系统>关于 进行查看。

如果你用的是更早的版本,而且不想升级到1703版,你仍然可以试一试,不过你要把你的Windows Subsystem for Linux从ubuntu 14 升级到ubuntu 16.

Windows Subsystem for Linux只能安装到系统盘。如果你的系统盘空间不够(Windows Subsystem for Linux需要3-4G的空间),那你可以使用这里的工具拓展你的系统分区。

动手

首先,我们要启动Windows Subsystem for Linux,然后在visual studio里使用bash 命令行界面。
Windows Subsystem for Linux
官方的Windows Subsystem for Linux安装指南在这里可以找到。不过,我们提供了下面的简化版本:

  1. 在管理员模式下(右击然后选择以管理员运行/Run as Administrator)打开PowerShell,然后执行以下命令:Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
  2. 按照提示重启计算机。
  3. 打开开发者模式
    • 打开设置面板,进入升级&安全>开发者 页面
    • 选择开发者模式Developer Mode单选按钮
    • 如果有提示,则重启计算机。
  4. 打开windows 命令提示器,输入bash。接受证书协议,然后会下载一张用户模式图片,并提取到%localappdata%\lxss\。然后会提示你设置linux用户名和密码。当这步骤结束的时候,快捷名称Bash on Ubuntu on Windows会被加入到你的开始菜单。
  5. 使用下面任一方式启动一个新的ubuntu shell:
    • 在命令提示器内输入bash,或者
    • 在开始菜单里使用Bash on Ubuntu on Windows。
  6. 一旦你进入了linux shell,要确保你运行了ubuntu 16:
    lsb_release -a
  7. 最后,升级 ubuntu:
    sudo apt update
    sudo apt full-upgrade

重装ubuntu

下面的步骤是可选的,不一定非做不可。只有在你需要从头开始,重装ubuntu的时候才需要做。

  1. 打开windows 命令行提示器,运行:
    lxrun /uninstall /full 进行卸载
    lxrun /install 进行重装
  2. 运行bash,启动新的ubuntu shell,然后在这个新的ubuntu shell里进行更新和升级:
    sudo apt update
    sudo apt full-upgrade

Visual Studio Code

从官网下载并安装visual studio code
https://code.visualstudio.com/Download 要想在visual studio code 内启用ubuntu bash 控制台,你需要修改用户设置:

  • 进入 File > Preferences > User Settings ,用户>偏好设置>用户设置。
  • 在右边面板里添加如下内容,覆盖掉默认设置:"terminal.integrated.shell.windows": "C:\Windows\sysnative\bash.exe".
  • 保存修改,你就可以使用Ctrl + ' 或者 View > Integrated Terminal切换到ubuntu 控制台。

你还可以添加c++拓展到visual studio code,这对c++开发有帮助。使用Ctrl + Shift + X打开拓展面板,然后添加以下拓展:

  • C/C++
  • C++ Intelisense
  • CMakeTools
  • CMake Tools Helper
  • Code Runner

编译源码

在windows文件系统上为EOS创建一个workspace 文件夹。在这篇文章中,我们使用X:\Workspaces\EOS,不过你可以自己决定你想在哪建这个workspace文件夹。
在linux 文件系统中,上面的地址会被映射为/mnt/x/Workspaces/EOS。它会相应地映射你设置的地址。
注意:使用分区名称的小写字母形式,比如我们使用的是/mnt/x/。
到了这一步,你就可以开始使用visual studio code的ubuntu shell了(View > Integrated Terminal)。它的主要优势是它能方便地复制黏贴。

下面的所有命令都是在ubuntu shell上运行的。

  1. 定义以下系统变量:
    export WORKSPACE_DIR=/mnt/x/Workspaces/EOS
    export EOSIO_INSTALL_DIR=${WORKSPACE_DIR}/eos
    export EOS_PROGRAMS=${EOSIO_INSTALL_DIR}/build/programs
    export TEMP_DIR=/tmp

    注意:把x/Workspaces/EOS 替换成你设置的workspace地址。

  2. 把上面设置的系统变量保存到~/.bashrc文件:
    echo "export WORKSPACE_DIR=${WORKSPACE_DIR}" >> ~/.bashrc
    echo "export EOSIO_INSTALL_DIR=${EOSIO_INSTALL_DIR}" >> ~/.bashrc
    echo "export EOS_PROGRAMS=${EOS_PROGRAMS}" >> ~/.bashrc
  3. 安装 cmake 和git:
    sudo apt install cmake
    sudo apt install git

    4.从EOS代码仓库clone 源代码:

    cd ${WORKSPACE_DIR}
    git clone https://github.com/eosio/eos --recursive

    5.现在,你就可以编译源码了。这一步可能需要几个小时时间,取决于你计算机的配置,而且,这期间会要求你确认某些动作,以及输入sudo密码。

    cd ${EOSIO_INSTALL_DIR}
    ./eosio_build.sh 

    注意:由于这一步需要从多个地方下载文件,它有可能会失败。如果失败的话,就重新开始这一步。

运行

如果以上的步骤没有什么错,你就编译好了EOS代码,它生成了多个文件和可执行文件。可执行文件放在$EOS_PROGRAMS文件夹:

  • nodeos: 区块链服务器节点生成组建
  • cleos: 和区块链交互的接口命令
  • keosd: EOS 钱包
  • eosio-launcher:节点网络组成和部署的应用

此贴为转载复制占坑,等稍后 重新整理下。

!wast.empty(): no wast file found eosio.token/eosio.token.wast

今天更新EOS到 release 1.1 重新编译后,发现执行eosio.token合约时提示

surou@surou-C-H110M-K-Pro:~/eos/contracts$ cleos set contract eosio.token eosio.token
Reading WAST/WASM from eosio.token/eosio.token.wast...
1595091ms thread-0   main.cpp:2760                 main                 ] Failed with error: Assert Exception (10)
!wast.empty(): no wast file found eosio.token/eosio.token.wast

查看目录下,确实没有生成 wast文件

surou@surou-C-H110M-K-Pro:~/eos/contracts/eosio.token$ ls
CMakeLists.txt  eosio.token.abi  eosio.token.cpp  eosio.token.hpp

手动编译生成wast

surou@surou-C-H110M-K-Pro:~/eos/contracts/eosio.token$ eosiocpp -o eosio.token.wast eosio.token.cpp 
surou@surou-C-H110M-K-Pro:~/eos/contracts/eosio.token$ ls
CMakeLists.txt  eosio.token.abi  eosio.token.cpp  eosio.token.hpp  eosio.token.wasm  eosio.token.wast

再次部署合约,已经OK

surou@surou-C-H110M-K-Pro:~/eos/contracts$ cleos set contract eosio.token eosio.token
Reading WAST/WASM from eosio.token/eosio.token.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 45460e0ebf7c0bde0a6802adec4dd7bc457c2d4a76f1d97fc6376c88d9db0f28  8032 bytes  1079 us
#         eosio <= eosio::setcode               {"account":"eosio.token","vmtype":0,"vmversion":0,"code":"0061736d01000000017e1560037f7e7f0060057f7e...
#         eosio <= eosio::setabi                {"account":"eosio.token","abi":"0e656f73696f3a3a6162692f312e3000030663726561746500020669737375657204...
warning: transaction executed locally, but may not be confirmed by the network yet

也许是编译有什么警告或者异常没注意,或者是EOS更新什么脚本导致的,暂时未做具体跟进,下次更新版本测试后再做补充。

push_transaction Error 3050003 eosio_assert_message assertion failure

查看nodeos 抛出log

Exception Details: 3050003 eosio_assert_message_exception: eosio_assert_message assertion failure
assertion failure with message: comparison of assets with different symbols is not allowed

是因为push_transaction中的代币符号与所需代币符号不一致

测试代码如下

eos = Eos({
        httpEndpoint: 'http://192.168.1.112:8888',
        chainId: 'cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f',
        keyProvider: wif,
        verbose: true
    })
const name = 'bcskillsurou'
    const pubkey = 'EOS68mvUMCz73a5Xj2wnJxdTW1aPjmiKCeSAjVdXjTd3D3g9A38EE'
     eos.transaction(tr => {
        tr.newaccount({
          creator: 'dapp.exec1',
          name,
          owner: pubkey,
          active: pubkey
        })

        tr.buyrambytes({
          payer: 'dapp.exec1',
          receiver: name,
          bytes: 8192
        })

        tr.delegatebw({
          from: 'dapp.exec1',
          receiver: name,
          stake_net_quantity: '10.0000 EOS',
          stake_cpu_quantity: '10.0000 EOS',
          transfer: 0
        })
    }).then(console.log)
    .catch(e => {
        console.error(e);
    })

由于测试网络中内置代币符号为 SYS,所以需要将上面的 EOS修改为 SYS
还有种就是将测试网内置代币修改为EOS
修改eos_source_dir/CMakeLists.txt
CORE_SYMBOL_NAME "SYS"修改为CORE_SYMBOL_NAME "EOS"重新编译项目

[EOS源码分析] EOS智能合约开发实践之数据库持久化

以太坊智能合约定义的全局变量的值是持久性的,就相当于智能合约一直在运行着。而EOS的智能合约更加接近我们平时使用的程序,每次执行action都相当于启动智能合约的一个新实例,一旦执行完,代码定义的变量就释放了,不会影响下一次执行环境。但是智能合约肯定需要有持久化存储的需求,比如永久保存智能合约代币的状态,不能代币转账执行完,代币的balance余额信息和转账前一样吧。这个持久化存储就是数据库存储数据。EOS允许智能合约定义自己的私有数据库表。比如下图,Apply Context的内容都是一次性的,一次action执行完成,对象就释放了,只有存储到EOSIO database的才被保存。

这个私有数据表是通过multi_index来访问和交互的。EOS的multi_index类似boost的multi_index,即多索引容器。有了多级索引,智能合约就具备了操作类似数据库模块的功能。eosio::multi_index支持主键,但是必须是唯一的无符号64位整数。eosio::multi_index中的对象容器按主键索引按无符号64位整数主键的升序排序。

申请私有数据表

定义一个multi_index对象即可

纯数据表

multi_index<table_name, record >
   > table( code, scope );

这里的code必须是contract的account_name, 因为这个数据表的操作api都有check,比如'modify' 函数.

void modify( const T& obj, uint64_t payer, Lambda&& updater ) {
         eosio_assert( _code == current_receiver(), "cannot modify objects in table of another contract" ); // Quick fix for mutating db using multi_index that shouldn't allow mutation. Real fix can come in RC2.
  }

scope参数,这个参数的初衷是用来实现交易action并行执行的。我们知道,action之间可能是存在依赖的,比如alice转给bob 5块钱(action1),alice转给james 5块钱(action2), action1和action2就有依赖关系。因为如果Alice只有6块钱,那么这两个action中必然有一个action是失败的。如果这两个action并行执行那这两个都会成功,明显是不合理的。你可能会说可以使用互斥锁解决冲突啊?事实上市不行的,申请互斥锁中的两段代码的执行顺序是随机的,所以导致action1和action2的执行顺序是随机的,这就违背了区块链的基本要求---确定性和可重复性,每个节点按相同顺序执行一个action列表,结果应该是一致的。所以为了解决这个问题就提出了scope的。假设这个balance的数据保存在‘balance’数据表里,scope为'balance_scope', 所以在提交action1时就会传入一个scope参数'balance_scope‘,这样系统根据action1和action的scope很容易知道action1, action2都会操作'balance_scope'对应的'balance'表,就不会让这两个action并行执行。其实,这里的核心问题是action操作的数据表的信息,如果提交的action不告知系统,系统必须要执行完action才能知道操作了哪个数据表,这自然没法实现并行。当然,目前版本scope基本没有起作用,action并行老版本有实现过的框架(但不起作用),最新的版本框架甚至已经删除了相关逻辑,看来action的并行化路漫慢而修远兮。

纯数据表示例

eosio.token合约

struct currency_stats {
    asset supply;
    asset max_supply;
    ccount_name issuer;

    //必须有该函数
    uint64_t primary_key()const { return supply.symbol.name(); }
};
typedef eosio::multi_index<N(stat), currency_stats> stats;

主键查找数据

纯数据表只能通过table.find函数以主键为参数查找数据

stats statstable( _self, sym.name() );
auto existing = statstable.find( sym.name() );

多索引数据表

基本格式
multi_index<table_name, record,
        indexed_by<index_name, index_func>,
        indexed_by<index_name_1, index_func_1>,
        ….>
   > table( code, scope );

和纯数据表相比,就多了index_by索引定义
索引的定义格式是:indexed_by<索引名,索引键值函数>,比如这个实例:

eosio::multi_index<N(orders), limit_order,
        indexed_by< N(byexp), const_mem_fun<limit_order, uint64_t, &limit_order::get_expiration> >,
        indexed_by< N(byprice), const_mem_fun<limit_order, uint128_t, &limit_order::get_price> >
                     > orders( N(multitest), N(multitest) );

<N(orders), limit_order,这部分和纯数据表是一样的,第一个参数是表名,第二个参数是表行对象。这里有两个索引对象。分别是
indexed_by< N(byexp), const_mem_fun<limit_order, uint64_t, &limit_order::get_expiration>>
indexed_by< N(byprice), const_mem_fun<limit_order, uint128_t, &limit_order::get_price> >
N(byprice):索引名称是'byprice'
const_mem_fun<limit_order, uint128_t,&limit_order::get_price>中的const_mem_fun定义了一个索引键值函数,这个其实是通过boost::bind函数将对象的const成员函数转化为函数指针.其格式是<ObjectType, indexType(索引类型), &ObjectType::function>

索引查找数据

  auto priceidx = orders.get_index<N(byprice)>();
    print("Items sorted by price:\n");
    for( const auto& item : priceidx ) {
       print(" ID=", item.id, ", price=", item.price, ", owner=", name{item.owner}, "\n");
    }

二级索引类型限制

  • idx64 - 原始的64位无符号整数密钥.
  • idx128 - 原始128位无符号整数密钥.
  • idx256 - 256位固定大小的字典键.
  • idx_double - 双精度浮点键.
  • idx_long_double - 四倍精度浮点键.

查看数据表数据

$cleos get table cleos get table [OPTIONS] contract scope table

实例运行

源码
class tabletest : public eosio::contract {
    private:
        /// @abi table
         struct contact {
            account_name name;
            uint64_t     phone;

            auto primary_key()const { return name; }
            uint64_t get_phone()const { return phone; }

            EOSLIB_SERIALIZE( contact, (name)(phone))
        };
        typedef eosio::multi_index<N(contact), contact, indexed_by< N(byphone), const_mem_fun<contact, uint64_t, &contact::get_phone> >
                  > contacts;

    public:
        using contract::contract;

        /// @abi action
        void add(account_name owner, account_name name, uint64_t phone ) {
            //新增数据项
            contacts contacttable( _self, owner );//(code, scope)
            //(player, item),player域指由谁来支付这个数据存储费用
            contacttable.emplace( _self, [&]( auto& o ) {
                o.name = name;
                o.phone = phone;
            });
        }

        /// @abi action
        void remove(account_name owner, account_name contact_name) {
            //删除数据项
            contacts contacttable( _self, owner );
            name n{contact_name};
            auto item = contacttable.find( contact_name );
            if( item == contacttable.end() ) {
                print_f("Not found name:%sn", n.to_string().data());
            } else {
                contacttable.erase(item);
            };
        }

        /// @abi action
        void modify(account_name owner, account_name contact_name, uint64_t phone) {
            //修改数据项
            contacts contacttable( _self, owner );
            name n{contact_name};
            auto item = contacttable.find( contact_name );
            if( item == contacttable.end() ) {
                print_f("Not found name:%sn", n.to_string().data());
            } else {
                contacttable.modify( item, _self, [&]( auto& o ) {
                    o.phone = phone;
                });
            };
        }

        /// @abi action
        void findbyname(account_name owner, account_name contact_name) {
            //根据name主键查找数据项
            contacts contacttable( _self, owner );
            name n{contact_name};
            auto item = contacttable.find( contact_name );
            if( item == contacttable.end() ) {
                print_f("Not found name:%s\n", n.to_string().data());
            } else {
                n = name{item->name};
                print_f("Found phone:%, for %s\n", item->phone, n.to_string().data());
            };
        }

        /// @abi action
        void findbyphone(account_name owner, uint64_t phone) {
            //根据phone索引查找数据项
            contacts contacttable( _self, owner );
            auto phoneinx = contacttable.get_index<N(byphone)>();
            auto item = phoneinx.find(phone);
            if( item == phoneinx.end() ) {
                print_f("Not found phone:%\n", phone);
            } else {
                name n{item->name};
                print_f("Found name:%s for phone:%\n", n.to_string().data(), item->phone);
            };
        }
};

EOSIO_ABI( tabletest, (add) (remove) (modify) (findbyname) (findbyphone))

编译

请参考【EOS编写HelloWorld智能合约】

执行

$cleos create account eosio table.code $KEY_PUB_1 $KEY_PUB_1
$cleos set contract table.code ./table -p table.code
$cleos create account eosio itleaks $KEY_PUB_2 $KEY_PUB_2
$cleos push action table.code add '[ "itleaks", "jackma", "13456" ]' -p itleaks
$cleos get table table.code itleaks contact

源码一键实践

https://github.com/itleaks/eos-contract/tree/master/table-exp

转载自:http://blog.csdn.net/itleaks

[EOS源码分析] EOS保留权限eosio.code深度解读

inline action简单来说就是action调用另外一个action, 具体来说就是一个智能合约的代码调用另外一个智能合约的函数。
eoiso.code这一特殊权限是dawn4.0后新增的内部特殊权限,用来加强inline action的安全性。比如alice调用智能合约contract1.test,一开始alice看过contract1.test的逻辑,发现它只是一个打印函数,并不会调用其他合约。所以alice以自己active的权限alice@active去执行contract1.test。但是contract1的拥有者某一天可能偷偷更改了test的实现,在test函数中调用eosio.token的transfer函数以alice@active权限就可以取走alice的EOS. 为了解决权限乱用问题,EOS新增了eosio.code这个特殊权限。采用eosio.code后,contract1.test要以alice@active去调用eosio.token,必须得到alice的授权,即必须在alice@active里添加contrac1@eosio.code授权

$cleos set account permission alice active '{"threshold": 1,"keys": [{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":1}],"accounts": [{"permission":{"actor":"contract1","permission":"eosio.code"},"weight":1}]}' owner -p alice@owner

即用户调用push action -p permission授权的权限只作用域该action,要用到其他action必须再授权eosio.code

Inline action权限分析

我们以【inline action开发实践】博文中的实例为例,hello.code智能合约调用hello.target合约

class hello : public eosio::contract {
  public:
    using contract::contract;
    /// @abi action 
    void hi( account_name from, account_name to) {
        require_auth(from);
        print( "Hello, from:", name{from}, ", to:", name{to});
        action(
            //这里{to, active}必须授权给{_self, eosio.code}
            permission_level{to, N(active)},
            //调用 hello.target合约 的'callme' action
            N(hello.target), N(callme),
            std::make_tuple(to)
         ).send();
    }
};

通过下面的命令执行hello.code智能合约
$cleos push action hello.code hi '["args.user","args.user1"]' -p args.user
调用时序图如下

时序图中有2次调用check_authorization

  • 3中的check_authorization是检测交易的签名是否满足action 'hello.code@hi'调用的权限声明args.user@active,这个检测机制已经在【EOS权限机制】一文已经详细分析过了
  • 11中的check_authorization是检测hello.code@eosio.code是否满足action "hello.target@callme"的权限声明to@active(这里的to='args.user1'),也就是args.user1@active。所以为了让这个inline action调用成功,必须添加如下授权
    $cleos set account permission args.user1 active '{"threshold": 1,"keys": [],"accounts": [{"permission":{"actor":"hello.code","permission":"eosio.code"},"weight":1}]}' owner -p args.user1@owner

    这里有个疑问,为啥是检测(hello.code, eosio.code)授权是否满足权限声明呢?我们仔细看下上面的hi代码

    void hi( account_name from, account_name to) {
          require_auth(from);
          print( "Hello, from:", name{from}, ", to:", name{to});
          action(
              //这里{to, active}必须授权给{_self, eosio.code}
              permission_level{to, N(active)},
              //调用 hello.target合约 的'callme' action
              N(hello.target), N(callme),
              std::make_tuple(to)
           ).send();
      }

    当hello.code智能合约代码通过action.send调用其他智能合约时,hi代码是拿不到任何私钥的,也就没法为声明的权限签名,即没法证明该智能合约具备action声明的权限to@active。因此,只有系统代码做担保了,因而系统提出了一个虚拟权限eosio.code。然后系统直接告诉系统检验逻辑(authorization_manager) ,‘action(hello.target)’已经具备hello.code@eosio.code权限。然后authorization_manager只需检验to@active是否授权给hello.code@eosio.code即可。通过这种虚拟的权限证明解决了合约调用合约的权限检测问题

    上面红色的部分就是系统代为担保的权限证明。对于用户直接提交的action,这个权限证明是从transaction的签名里恢复出来的

    转载自:http://blog.csdn.net/itleaks