1

I am trying to implement a smart contract for identity management use case.

I am able to pass all the test cases which I have written in mocha with truffle.

I am compiling this smart contract using truffle and calling it from web3.py.

But when I add an entry in the mapping ( sicontracts ) with key as bytes32 ( Ipfs hash converted to bytes32 ) and value as struct SIContract in the function submitContract . It seems to be not getting saved in the mapping. When I verify this in function approveContract it always returns false. When I checked I could find that my mapping is not getting saved. Please help.

Solidity Code :

pragma solidity ^0.4.0;

 /**
 * The purpose of this contract is to provide a mechanism to verify idntity
 */

contract SmartIdentityRegistry {

address private owner;
uint constant PENDING = 0;
uint constant ACTIVE = 1;
uint constant REJECTED = 2;

/**
 * Constructor of the registry.
 */
function SmartIdentityRegistry() {
    owner = msg.sender;
}

/**
 * The SIContract structure: every SIContract is composed of:
 * - Hash of contract bytecode
 * - Account that submitted the address
 * - Status - 0 = pending, 1 = active, 2 = rejected.
 */
struct SIContract {
    bytes32 hash;
    address submitter;
    uint status;
}

/**
 * Mapping for contract registry.
 */
mapping(bytes32 => SIContract) public sicontracts;

/**
 * The only permission worth setting; doing the reverse is pointless as a contract
 * owner can interact with the contract as an anonymous third party simply by using
 * another public key address.
 */
modifier onlyBy(address _account) {
    if (msg.sender != _account) {
        revert();
    }
    _;
}

/**
 * Anyone can submit a contract for acceptance into a registry.
 */
function submitContract(bytes32 _contractHash, address idOwner) returns(bool) {
    var sicontract = sicontracts[_contractHash];
    sicontract.hash = _contractHash;
    sicontract.submitter = idOwner;
    sicontract.status = PENDING;
    return true;
}

/**
 * Only the registry owner (ideally multi-sig) can approve a contract.
 */
function approveContract(bytes32 _contractHash) returns(bool) {
    var sicontract = sicontracts[_contractHash];
    if(sicontract.submitter != msg.sender){
        return false;
    }
    sicontract.status = ACTIVE;
    return true;
}

/**
 * Only the registry owner (ideally multi-sig) can reject a contract.
 */
function rejectContract(bytes32 _contractHash) onlyBy(owner) returns(bool) {
    var sicontract = sicontracts[_contractHash];
    sicontract.status = REJECTED;
    return true;
}

/**
 * Only the registry owner and original submitter can delete a contract.
 * A contract in the rejected list cannot be removed.
 */
function deleteContract(bytes32 _contractHash) returns(bool) {
    var sicontract = sicontracts[_contractHash];
    if (sicontract.status != REJECTED) {
        if (sicontract.submitter == msg.sender) {
            if (msg.sender == owner) {
                delete sicontracts[_contractHash];
                return true;
            }
        }
    } else {
        revert();
    }
}

/**
 * This is the public registry function that contracts should use to check
 * whether a contract is valid. It's defined as a function, rather than .call
 * so that the registry owner can choose to charge based on their reputation
 * of managing good contracts in a registry.
 *
 * Using a function rather than a call also allows for better management of
 * dependencies when a chain forks, as the registry owner can choose to kill
 * the registry on the wrong fork to stop this function executing.
 */
function isValidContract(bytes32 _contractHash) returns(bool) {
    if (sicontracts[_contractHash].status == ACTIVE) {
        return true;
    }
    if (sicontracts[_contractHash].status == REJECTED) {
        revert();
    } else {
        return false;
    }
}
}

Python code of calling submit function

 self.smartIdContract.call({"from": sender_account}).submitContract(hex_hash,decode_hex(id_public_key))

Code for verifying above submitted entry

self.smartIdContract.call({"from": wrong_addr}).approveContract(id_hash)

What am I doing wrong ? Why my mocha test cases work fine and web3.py calls fails ? I am using testrpc as network.

My mocha test cases for the above contract is :

/**
 * The purpose of this test contract is to test the functions in      SmartIdentityRegistry.sol.
 */

var SmartIdentityRegistry = artifacts.require("SmartIdentityRegistry");

contract('SmartIdentityRegistry', function(accounts) {

var registry,
    contractRegistry1,
    contractRegistry2,
    contracthash1,
    contracthash2;

contracthash1 = '0xca02b2202ffaacbd499438ef6d594a48f7a7631b60405ec8f30a0d7c096d54d5';
contracthash2 = '0xca02b2202ffaacbd499438ef6d594a48f7a7631b60405ec8f30a0d7c096dc3ff';

before("Setup the Smart Identity registry and hydrate the required variables", function(done) {
    contractRegistry1 = accounts[0];
    contractRegistry2 = accounts[1];

    SmartIdentityRegistry.new({from: contractRegistry1})
    .then(function(response) {
        registry = response;
        done();
    });

    return registry,
    contractRegistry1,
    contractRegistry2;
});

describe("SmartIdentityRegistry tests", function() {

    it("will submit a contract into the registry", function() {
        return registry.submitContract(contracthash1,contractRegistry2, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
    });

    it("will prove that the submitter can only  approve a contract", function() {
        return registry.approveContract(contracthash1, {from: contractRegistry2})
        .then(function(response) {
            assert.isOk(response, 'Contract approval failed');
        });
    });


    it("will prove that a non-id owner cannot approve a contract", function() {
        return registry.approveContract(contracthash1, {from: contractRegistry1})
        .catch(function(error) {
            assert.isOk(error, "Expected error has not been caught");
        });
    });

    it("will prove that the registry owner can reject a contract", function() {
        return registry.rejectContract(contracthash1, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract rejection failed');
        });
    });

    it("will prove that a non-owner cannot reject a contract", function() {
        return registry.rejectContract(contracthash1, {from: contractRegistry2})
        .catch(function(error) {
            assert.isOk(error, "Expected error has not been caught");
        });
    });

    it("will delete a contract from the registry", function() {
        registry.submitContract(contracthash2,contractRegistry2, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
        return registry.deleteContract(contracthash2).then(function(response) {
            assert.isOk(response, 'Contract failed to be deleted');
        });
    });

    it("will verify a contract is not valid", function() {
        registry.submitContract(contracthash2,contractRegistry2, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
        return registry.isValidContract(contracthash2).then(function(response) {
            assert.isOk(response, 'Failed to verify contract');
        });
    });

    it("will verify a contract is not valid and will throw an error", function() {
        registry.submitContract(contracthash2,contractRegistry2, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
        registry.rejectContract(contracthash2, {from: contractRegistry1});
        return registry.isValidContract(contracthash2)
        .catch(function(error) {
            assert.isOk(error, "Expected error has not been caught");
        });
    });

    it("will verify a contract is valid", function() {
        registry.submitContract(contracthash2, contractRegistry2,{from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
        registry.approveContract(contracthash2, {from: contractRegistry2});
        return registry.isValidContract(contracthash2)
        .then(function(response) {
            assert.isOk(response, 'Failed to verify contract');
        });
    });
});

 });
Bill Goldberg
  • 241
  • 1
  • 3
  • 8

1 Answers1

1

Instead of a call, you want a transaction. See: What is the difference between a transaction and a call?

So instead of:

self.smartIdContract.call({"from": sender_account}).submitContract(...)

Try:

self.smartIdContract.transact({"from": sender_account}).submitContract(...)

Or the newer syntax from Web3.py v4:

bound_submission = self.smartIdContract.functions.submitContract(...)
txn_hash = bound_submission.transact({"from": sender_account})
carver
  • 6,381
  • 21
  • 51
  • Thanks Carver. How will I get the result then in the second verification ? Should I use event or can I have this without changing above contract ? – Bill Goldberg Nov 15 '17 at 20:11
  • Or can I make call and say it is verified ? – Bill Goldberg Nov 15 '17 at 20:47
  • After your second transaction, you could wait for it to be mined and then call isValidContract. But yes, an event might be even better here. – carver Nov 16 '17 at 02:49
  • This is a web app user first submit id hash and verifies with approving after that. 2 consecutive steps. How can I wait untill mined ? Is there blocking call ? My plan is. As follows first transact on submit contract and call on second function. – Bill Goldberg Nov 16 '17 at 02:53
  • Waiting for the event sounds like your best option. – carver Nov 16 '17 at 04:47