基于solidity的共享物品管理系统合约调试过程

这份合约包括创建物品、租用物品、支付押金租金返还押金等功能。这个需求其实很简单,很适合学习和参考。合约并非原创,感谢我的师兄对本合约的帮助!如果有语法还不清楚的朋友请参看solidity官方文档。

代码更新于2018.5.8   14:52分

pragma solidity ^0.4.4;


contract myShare {
struct Renter{
address addr;//renter's address
uint since;//from which time?
}

struct Object{
address creator;//the owner of obj
string name;// name of obj
uint priceDaily;//price of each day
uint deposit;//deposits of the obj
Renter renter;//who rent the obj
bool rented;//if rented?
string detail;//another infomation
}

struct Namekey{
uint[] keys;//storage the name's keys
}


uint[] private ids; //to return the ids of obj
uint public numObjects;
mapping(uint => Object) private objects;
mapping(string => Namekey) private nameToKeys;
address public owner;
event NewObject(uint objID,address creator);

modifier objectInRange(uint objID) {//modify function
        if (objID >= numObjects)
            throw;
        _;
}

modifier onlyOwner(){
    if(msg.sender != owner){
    throw;
    }
    _;
}

function myShare() {
owner = msg.sender;
}

function objectIsRented(uint objID) objectInRange(objID) returns (bool){
return objects[objID].rented;
}

function createObj(string name,uint priceDaily,uint deposit,string detail){
Object newObject = objects[numObjects];
nameToKeys[name].keys.push(numObjects);//add the key to the name's key

newObject.creator = msg.sender;
newObject.name = name;
newObject.priceDaily = priceDaily;
newObject.rented = false;
newObject.deposit = deposit;
newObject.detail = detail;

NewObject(numObjects,msg.sender);
ids.push(numObjects);
numObjects++;
}

function rentObj(uint objID) payable objectInRange(objID) returns(bool){
if(objectIsRented(objID) || msg.value < objects[objID].deposit || msg.sender == objects[objID].creator){
throw;
}
objects[objID].renter = Renter({addr:msg.sender, since:now});  //record the info of the renter
uint rest = msg.value - objects[objID].deposit;
if(!objects[objID].renter.addr.send(rest)){   // return the rest balance
throw;
}
objects[objID].rented = true;
return true;
}

function returnObj(uint objID) payable objectInRange(objID) returns (bool){
if(!objects[objID].rented){
throw;
}
//if(objects[objID].renter.addr != msg.sender){
// throw;
//}
uint duration = (now - objects[objID].renter.since) / (24*60*60*1.0);
if(duration == 0){
   duration = 1;
}
uint charge = duration * objects[objID].priceDaily;
if(!objects[objID].creator.send(charge)){ //return the charge to creator
throw;
}
if(!objects[objID].renter.addr.send(objects[objID].deposit - charge)){ //return the rest deposit to renter
throw;
}
delete objects[objID].renter;
objects[objID].rented = false;
return true;
}

function getBalance(address addr) returns (uint){  

  

                 return addr.balance;

}


function findNames(string name) constant returns(uint[]){
return nameToKeys[name].keys;
}

function getNumObjects() constant returns(uint){
return numObjects;
}

function getObjectIds() constant returns(uint[]){
return ids;
}

function getObjectName(uint objID) objectInRange(objID) returns(string objName){
var obj = objects[objID];
objName = obj.name;
}

function getObjectCreator(uint objID) constant objectInRange(objID) returns(address){
return objects[objID].creator;
}

function getObjectPriceDaily(uint objID) constant objectInRange(objID) returns(uint){
return objects[objID].priceDaily;
}

function getObjectDeposit(uint objID) constant objectInRange(objID) returns(uint){
return objects[objID].deposit;
}

function getObjectRenterAddress(uint objID) constant objectInRange(objID) returns(address){
return objects[objID].renter.addr;
}

function getObjectRenterSince(uint objID) constant objectInRange(objID) returns(uint){
return objects[objID].renter.since;
}

function getObjectDetail(uint objID) constant objectInRange(objID) returns(string){
return objects[objID].detail;
}

function remove() onlyOwner {
selfdestruct(owner);
}

}


这份合约运行在Remix IDE上(一个在线的Solidity编译器很适合新手入门,直接百度进入)。如何编译运行参照点击打开链接

        (下面这段解释我这个实验是怎么设计的。以及为什么这样设计)

        我发现了一个很棘手的问题,创建合约和执行合约中的某个函数都要消耗gas,(什么是gas请参看点击打开链接),会给我的实验带来麻烦,我设计的验证方案是,账户1创建一个物品,账户2来租用,然后归还,跟踪整个过程中以太币的去向。但是比如账户2归还的时候一定会消耗gas,也就会消耗余额同时押金返还会增加余额,尽管日志中有gas消耗具体数值,但是实在不会计算。而账户1在创建物品后,不再需要调用任何函数,所以只能判断账户1的余额具体变化。应该有一个账户3,专门用来创建合约和调用函数的。简单来说就是为了跟踪余额,尽量排除gas带来的影响!但是到最后我设计的方案还是无法具体检测用户2的余额变化,因为消耗gas。如果有更好的实验方案请提出来谢谢!

一、创建合约

基于solidity的共享物品管理系统合约调试过程

二、创建一个物品(注意创建物品选用第一个账户  0xca35b7d915458ef540ade6068dfe2f44e8fa733c

在函数中输入物品参数。

基于solidity的共享物品管理系统合约调试过程

我们创建了第一个物品 自行车,创建成功。

三、租用一个物品试一试(这里选用第二个账户租用 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c

基于solidity的共享物品管理系统合约调试过程基于solidity的共享物品管理系统合约调试过程

然后点解rentObj,在控制台中查看details,返回值是true物品就租借成功了。这时候查看一下账户2的余额,注意:不要用账户2来getBalance,用账户5来调用getBalance,参数输入账户2的地址,这样保证账户2的以太币仅用来支付和返还租金。

得到的账户2余额如下:

基于solidity的共享物品管理系统合约调试过程

账户1的余额如下:

基于solidity的共享物品管理系统合约调试过程

合约地址的余额如下:

基于solidity的共享物品管理系统合约调试过程

这个过程告诉我们账户2的10个以太币转移到了合约上(其实合约也是一个地址,和各个账户一样),合约暂为保管。合约地址就是memory前面那个,如果对这个地方很晕,建议再看看关于以太坊地址的资料,弄懂这些地址都是什么。

四、归还物品

基于solidity的共享物品管理系统合约调试过程

这里注意要选择账户2来归还物品,无法避免消耗账户2的gas。这时候我们再来看一下各个账户的余额(调用getBalance函数时都要用帐号5否则会影响实验结果)

账户1:

基于solidity的共享物品管理系统合约调试过程

账户2:

基于solidity的共享物品管理系统合约调试过程

合约账户:

基于solidity的共享物品管理系统合约调试过程

与物品归还前相比,账户1里面尾数由794684变成了794685,多了一位以太币,就是租金1元/天,不足一天按一天计算。

账户2由9907792变成9794685,押金退还反而少了?为什么呢,因为归还物品的时候调用了returnObj函数,消耗了gas。

合约账户余额变成了0,因为10位押金本来就是暂时保存的,1位给了账户1,9位返还给了账户2。


这个实验过程的不足之处就是没有弄清楚账户2的变化,不理解以太币的单位eth和wei,以及gas和以太币之间的关系。虽然不严谨,至少账户1收到了租金,也证实了押金的整个过程,实现了去中心化的一次租借过程。


最后希望大家多提意见,有疑问请留言,有更好的方案请提出来谢谢!