예제 #1
0
    def select_witness(self, transaction, i):
        def gte(a, b):
            if a == b:
                return True
            for i in range(0, 31):
                if a[i] > b[i]:
                    return True
                if a[i] < b[i]:
                    return False
            return True

        hash = nacl.hash.sha256(transaction + i.to_bytes(4, byteorder='little', signed=True), encoder=nacl.encoding.RawEncoder)

        node_list = list(self.addressList.keys())

        node_list.append(self.verify_key)
        tx = trustchain_pb2.Transaction()
        tx.ParseFromString(transaction)
        for ID in tx.ID:
            try:
                node_list.remove(bytes(ID))
            except KeyError:
                pass

        node_list.sort()
        witness = node_list[0]
        for node in node_list:
            if gte(node, hash):
                return witness
            if gte(node, witness):
                witness = node
        return witness
예제 #2
0
    def witness_transaction(self, TransactionProposal):
        reply = b"\x00" # Default to nack

        request = trustchain_pb2.SignatureRequest()
        request.ParseFromString(TransactionProposal)
        transaction = trustchain_pb2.Transaction()
        transaction.ParseFromString(request.transaction)
        logging.info("received witness request")

        if self.select_witness(request.transaction, request.witnessCount, ) != self.verify_key:
            logging.error("prescribed witness ID does not match my ID")
            return reply

        # do this check for every party associated with the transaction
        try:
            for j in range(0, len(transaction.ID)):
                self.validate_transaction(transaction.ID[j], transaction, request.chains[j].blocks[0])
        except ValueError as e:
            logging.exception("A problematic transaction was detected during witnessing \n")
            return reply

        signature = trustchain_pb2.Approval()
        signature.ID = self.verify_key
        signature.i = request.witnessCount
        signature.signature = self.signing_key.sign(request.transaction).signature
        reply = signature.SerializeToString()
        logging.info("Transaction ")
        return reply
예제 #3
0
        def transactions():
            start = request.args.get('from')
            if not start or start == "":
                next = client.chain[-1]
            else:
                next = base64.urlsafe_b64decode(start.encode('UTF-8'))

            length = request.args.get('length')
            if not length:
                length = 25
            else:
                length = int(length)

            length = min(length, len(client.chain)-3)

            transactions = []
            for entries in range(length):
                block_bytes = client.chain[next][:-32]
                block = trustchain_pb2.Block()

                block.ParseFromString(block_bytes)
                transaction = trustchain_pb2.Transaction()
                transaction.ParseFromString(block.transaction)
                myID = None
                for i in range(len(transaction.ID)):
                    if transaction.ID[i] == bytes(client.verify_key):
                        myID = i
                if myID == 1:
                    value = transaction.value * -1
                else:
                    value = transaction.value
                next = transaction.previousHash[myID]
                transactions.append(((time.strftime("%b %d %Y %H:%M:%S",
                    time.gmtime(transaction.timestamp))),
                    client.friendlyNameList[transaction.ID[1-myID]], "EUR {0:.2f}".format(value), base64.urlsafe_b64encode(next).decode('UTF-8')))

                if next == b'\x00':
                    break

            return jsonify({"from": base64.urlsafe_b64encode(next).decode('UTF-8'), "transactions": transactions}), 200, {'Access-Control-Allow-Origin': '*'}
예제 #4
0
    def validate_transaction(self, ID, transaction, block):
        # A function that checks if the current transaction is a valid successor of the block for this specific node
        hash = block[-32:]

        # Find the index of node in current transaction
        index = -1
        for i in range(0, len(transaction.ID)):
            if transaction.ID[i] == ID:
                index = i
                break
        if index == -1:
            raise ValueError("Node is not owner of this transaction")

        block, x = self.trustchain.verify_block(block)
        temp_transaction = trustchain_pb2.Transaction()
        temp_transaction.ParseFromString(block.transaction)

        if transaction.previousHash[index] != hash:
            raise ValueError("PreviousHash does not point to actual previous hash")

        # determine old balance
        for i in range(0, len(temp_transaction.ID)):
            if temp_transaction.ID[i] == transaction.ID[index]:
                previous_balance = temp_transaction.balance[i]

        for i in range(0, len(transaction.ID)):
            if transaction.balance[i] < 0:
                logging.error(transaction)
                raise ValueError("Negative balance detected:")

        # If index = 0, node is on the receiving side
        if index == 0:
            if transaction.balance[index] != previous_balance + transaction.value:
                logging.error(transaction)
                raise ValueError("Previous and current value do not match with new balance")
        # if index  == 1, node is on giving side
        elif index == 1:
            if transaction.balance[index] != previous_balance - transaction.value:
                logging.error(transaction)
                raise ValueError("Previous and current value do not match with new balance")
예제 #5
0
    def verify_block(block_bytes):
        hash = block_bytes[-32:]
        block_bytes = block_bytes[:-32]

        if hash != nacl.hash.sha256(block_bytes,
                                    encoder=nacl.encoding.RawEncoder):
            raise ValueError("Hashes do not match")
        block = trustchain_pb2.Block()
        block.ParseFromString(block_bytes)

        transaction = trustchain_pb2.Transaction()
        transaction.ParseFromString(block.transaction)

        if len(transaction.ID) != len(block.signatures):
            print(transaction)
            print(block)
            raise ValueError(
                "Number of signatures don't match with the number of ID's")

        for i in range(0, len(transaction.ID)):
            try:
                verify_key = nacl.signing.VerifyKey(transaction.ID[i])
                verify_key.verify(block.transaction, block.signatures[i])
            except nacl.exceptions.BadSignatureError as e:
                raise ValueError(e)

        if round(time.time()) < transaction.timestamp:
            raise ValueError(
                "Block claims to be created in the future. current time {}, creation time {}"
                .format(
                    time.strftime("%Y-%m-%d %H:%M:%S", (time.gmtime())),
                    time.strftime("%Y-%m-%d %H:%M:%S",
                                  (time.gmtime(transaction.timestamp)))))

        if transaction.value == 0:
            # Transaction is genesis block
            pass

        return block, hash
예제 #6
0
    def create_genesis_block(self):
        '''
        Genesis block:
        | 0x00 | Timestamp | creator ID | Signature | Hash |
            1  +  4 bytes  +  32 bytes  +  64 bytes +  32  =  133
        '''
        ID = bytes(self.signing_key.verify_key)
        block = trustchain_pb2.Block()
        # create empty transaction
        transaction = trustchain_pb2.Transaction()
        transaction.timestamp = 0
        transaction.value = 0
        transaction.ID.append(ID)
        transaction.previousHash.append(b'\0')
        transaction.sequence_no.append(0)
        transaction.balance.append(1000)

        block.transaction = transaction.SerializeToString()
        block.signatures.append(
            self.signing_key.sign(block.transaction).signature)
        serial_block = block.SerializeToString()
        hash = nacl.hash.sha256(serial_block, encoder=nacl.encoding.RawEncoder)
        return serial_block + hash
예제 #7
0
    def fsm(self, message):
        # # Statemachine implementation (See notes.pptx for diagram)
        # States for initiator node
        reply = b'\x00' # Always default to nack
        if self.state == 0:
            if message[:1] == b'\x01':
                trade_request = trustchain_pb2.TradeRequest()
                trade_request.ParseFromString(message[1:])

                self.counter_party = trade_request.publicKey
                self.trade_value = trade_request.value

                if self.counter_party not in self.addressList:
                    logging.warning("Incoming transaction from unknown id {}".format(self.counter_party))
                    return reply
                logging.info("Incoming transaction from {}".format(self.friendlyNameList[self.counter_party]))

                try:
                    counter_block, self.counter_party_prev_hash = self.trustchain.verify_block(trade_request.blocks[0])
                except ValueError:
                    logging.exception("Found invalid block in trade request")
                    return reply


                counter_transaction = trustchain_pb2.Transaction()
                counter_transaction.ParseFromString(counter_block.transaction)

                block_is_valid = False
                for i in range(0, len(counter_transaction.ID)):
                    if counter_transaction.ID[i] == self.counter_party:
                        self.counter_balance = counter_transaction.balance[i]
                        self.counter_party_sequenceNo = counter_transaction.sequence_no[i]
                        block_is_valid = True
                        break

                # Todo: re-enable balance check for normal usage
                if self.counter_balance < self.trade_value:
                    logging.warning("Value is more than balance! value is {}, received last block is: \n {} \n THIS WILL BE INGORED FOR DEMONSTRATION PURPOSES".format(self.trade_value ,str(counter_transaction)))
                    # return reply

                if block_is_valid:
                    transaction = trustchain_pb2.Transaction()
                    transaction.timestamp = round(time.time())

                    transaction.previousHash.append(self.chain[-1])
                    transaction.previousHash.append(self.counter_party_prev_hash)

                    transaction.sequence_no.append(self.sequenceNo + 1)
                    transaction.sequence_no.append(self.counter_party_sequenceNo + 1)

                    transaction.ID.append(bytes(self.signing_key.verify_key))
                    transaction.ID.append(self.counter_party)

                    transaction.value = self.trade_value

                    transaction.balance.append(self.balance + self.trade_value)
                    transaction.balance.append(self.counter_balance - self.trade_value)

                    # Create a block for the counter party to sign
                    block = trustchain_pb2.Block()
                    block.transaction = transaction.SerializeToString()
                    block.signatures.append(self.signing_key.sign(block.transaction).signature)
                    self.proposed_block = block

                    trade_reply = trustchain_pb2.TradeReply()
                    trade_reply.proposedBlock = block.SerializeToString()
                    trade_reply.blocks.append(self.chain[self.chain[-1]])

                    #  trade_ack, + last block
                    reply = b'\02' + trade_reply.SerializeToString()
                    self.state = 2
            return reply

        # Wait for trade reply
        if self.state == 1:
            if message[:1] == b"\x02":  # 2 is trade_reply
                trade_reply = trustchain_pb2.TradeReply()
                trade_reply.ParseFromString(message[1:])
                # Verify block
                try:
                    counter_block, self.counter_party_prev_hash = self.trustchain.verify_block(trade_reply.blocks[0])
                except ValueError:
                    return reply
                # todo: make the following code work for multiple blocks
                chain = trustchain_pb2.Chain()
                chain.blocks.append(self.chain[self.chain[-1]])

                counter_chain = trustchain_pb2.Chain()
                counter_chain.blocks.append(trade_reply.blocks[0])

                counter_transaction = trustchain_pb2.Transaction()
                counter_transaction.ParseFromString(counter_block.transaction)

                block_is_valid = False
                for i in range(0, len(counter_transaction.ID)):
                    if counter_transaction.ID[i] == self.counter_party:
                        self.counter_balance = counter_transaction.balance[i]
                        self.counter_party_sequenceNo = counter_transaction.sequence_no[i]
                        block_is_valid = True
                        break
                # shit down is copied from previous itteration
                try:
                    block = trustchain_pb2.Block()
                    block.ParseFromString(trade_reply.proposedBlock)

                    transaction = trustchain_pb2.Transaction()
                    transaction.ParseFromString(block.transaction)
                except ValueError as e:
                    print(e)
                    return reply

                if len(block.signatures) != 1:
                    print(transaction)
                    print(block)
                    raise ValueError("WTF, dit  block is helemaal niet getekend f****r")

                # Check if the values are actually the values you agreed uppon
                for i in range(0, len(transaction.ID)):
                    if self.trade_value != transaction.value:
                        raise ValueError("trade agreed on {}".format(self.trade_value))
                    # Checks for Counter party
                    if transaction.ID[i] == self.counter_party:
                        if self.counter_balance + self.trade_value != transaction.balance[i]:
                            block_is_valid = False
                            print(transaction)
                            print("Counter balance don't match old {}".format(self.counter_balance))
                        if self.counter_party_prev_hash != transaction.previousHash[i]:
                            block_is_valid = False
                            print(transaction)
                            print("Counter hash don't match {}".format(self.counter_party_prev_hash))
                        if self.counter_party_sequenceNo + 1!= transaction.sequence_no[i]:
                            block_is_valid = False
                            print(transaction)
                            print(" Counter sequence NO don't match {}".format(self.counter_party_sequenceNo))
                    # Checks for mine
                    if transaction.ID[i] == bytes(self.signing_key.verify_key):
                        if self.balance - self.trade_value != transaction.balance[i]:
                            block_is_valid = False
                            print(transaction)
                            print("my balance don't match old {}".format(self.balance))
                        if self.chain[-1][-32:] != transaction.previousHash[i]:
                            block_is_valid = False
                            print(transaction)
                            print("my hash don't match {}".format(self.chain[-1][-32:]))
                        if self.sequenceNo + 1 != transaction.sequence_no[i]:
                            block_is_valid = False
                            print(transaction)
                            print("My sequence NO don't match {}".format(self.sequenceNo))

                if block_is_valid:
                    block.signatures.append(self.signing_key.sign(block.transaction).signature)
                else:
                    return reply

                self.proposed_block = block
                if self.required_Witnesses > 0:
                    request = trustchain_pb2.SignatureRequest()
                    request.transaction = transaction.SerializeToString()

                    request.chains.add().CopyFrom(counter_chain)
                    request.chains.add().CopyFrom(chain)

                    gevent.spawn(self.collect_signatures, request, self.required_Witnesses, self.fwsp_reply)

                    try:
                        sigs = self.fwsp_reply.get(block=True, timeout=0.5)
                        if len(sigs) == 0:
                            logging.error("Transaction failed, not enough valid witnesses")
                            # print("Aaawh FWSP failed")
                            return reply
                    except gevent.timeout.Timeout:
                        logging.info("Sending keep alive message: not enough valid witness replies.")
                        reply = b'\x03' + self.fwsp_no_sigs.to_bytes(4,byteorder='big',signed=False)
                        self.state = 3
                        return reply

                    self.proposed_block.witnesses.extend(sigs)
                    logging.info("Transaction successful with {}/{} valid witness replies".format(len(sigs),
                                                                                              self.required_Witnesses))
                block_bytes = self.proposed_block .SerializeToString()
                block = block_bytes + nacl.hash.sha256(block_bytes, encoder=nacl.encoding.RawEncoder)

                self.chain[block[-32:]] = block
                self.chain[-1] = block[-32:]
                self.balance = self.balance - self.trade_value
                self.sequenceNo = self.sequenceNo + 1
                reply = b'\x05' + block
            return reply

        # Had to send keep alive message
        if self.state == 3:
            if message[:1] == b"\x04":
                try:
                    sigs = self.fwsp_reply.get(block=True, timeout=0.5)
                    if len(sigs) == 0:
                        logging.error("Transaction failed, not enough valid witnesses")
                        return reply
                except gevent.timeout.Timeout:
                    logging.info("Sending keep alive message: not enough valid witness replies.")
                    reply = b'\x03' + self.fwsp_no_sigs.to_bytes(4,byteorder='big',signed=False)
                    return reply

                self.proposed_block.witnesses.extend(sigs)
                logging.info("Transaction successful with {}/{} valid witness replies".format(len(sigs),
                                                                                              self.required_Witnesses))
                block_bytes = self.proposed_block.SerializeToString()
                block = block_bytes + nacl.hash.sha256(block_bytes, encoder=nacl.encoding.RawEncoder)

                self.chain[block[-32:]] = block
                self.chain[-1] = block[-32:]
                self.balance = self.balance - self.trade_value
                self.sequenceNo = self.sequenceNo + 1
                reply = b'\x05' + block
                return reply

        if self.state == 2:
            if message[:1] == b"\x05":
                block = message[1:]
                try:
                    received_block, hash = TrustChain.verify_block(block)
                except ValueError:
                    logging.exception("Malformed block received in final stage\n")
                    return reply

                if self.proposed_block.transaction != received_block.transaction:
                    logging.warning("Block does not contain transaction earlier agreed uppon")
                    return reply
                valid_witnesses = 0
                for aproval in received_block.witnesses:
                    try:
                        TrustChain.verify_witness_reply(received_block.transaction, aproval)
                        valid_witnesses += 1
                    except ValueError:
                        logging.exception("Error occured during the verification of witness replies in teh block")

                logging.info("Transaction successful with {}/{} witness".format(valid_witnesses, self.required_Witnesses))
                self.chain[block[-32:]] = block
                self.chain[-1] = block[-32:]
                self.balance = self.balance + self.trade_value
                self.sequenceNo = self.sequenceNo + 1
                reply = None
            elif message[:1] == b'\x03':
                logging.info("Received keep alive while waiting on signatures")
                reply = b"\x04"
            return reply