I was intending to answer another question, which was closed as a duplicate in the meantime, but I will use the case that was described there as an example to answer this question.
Assuming the transaction is in fact a standard transaction, the script does in fact reveal the sender of the transaction. According to the standard transaction description in the protocol specification the transaction has to first prove ownership of an output in order to claim it and spend it in the new transaction. The transaction that gave the sender the funds specifies the hash of a public key to which to send the funds. In this case it's the output script 1 of transaction f8c71a9f7...
:
OP_DUP OP_HASH160 496650cdb4b6275ca4478c0ce98cb6f7224bb1e7 OP_EQUALVERIFY OP_CHECKSIG
Notice the 496650cdb4b6275ca4478c0ce98cb6f7224bb1e7
in there. That is the hash of the public key. To claim this output, and then send it to you, the sender has to provide both the public key and a valid signature. So let's take a look at the script that claimed this output:
304402205e81e8ed0b1f7cf6d1d415961859d3b95f5e5c353af303b6cef1e3efa6c3349702202fa9fdd6914abd0e9606c78899e7f3010cafdad211645cf459ae18b3b827b2c101 0365e0beb9a0c1497f3667067aeb8f3ea9dc4c9d5696cee7f19eae49f9457a5cfb
This being a standard transaction it will conform to the format <sig> <pubKey>
, so the public key is 0365e0beb9a0c1497f3667067aeb8f3ea9dc4c9d5696cee7f19eae49f9457a5cfb
. To verify this we check an see if the hashes match (python code ahead):
script = "304402205e81e8ed0b1f7cf6d1d415961859d3b95f5e5c353af303b6cef1e3efa6c3349702202fa9fdd6914abd0e9606c78899e7f3010cafdad211645cf459ae18b3b827b2c101 0365e0beb9a0c1497f3667067aeb8f3ea9dc4c9d5696cee7f19eae49f9457a5cfb".split()
h = hashlib.sha256(script[1].decode("hex")).digest()
ripe160 = hashlib.new('ripemd160')
ripe160.update(h)
d = ripe160.digest()
print d.encode("hex")
This should result in 496650cdb4b6275ca4478c0ce98cb6f7224bb1e7
, which is the hash we have seen in the output script of the claimed out that was finally sent to you. Notice that to calculate this we did not have to fetch the transaction f8c71a9f7...
but merely relied on information you could get from the transaction that you received. Now since an address is not much more than the hash of a public key we can simply build an address from the information we have gathered so far (having the hash already we start from step 4 in the address construction):
#Prepend the Mainnet prefix
address = ('\x00' + d)
#Calculate checksum
checksum = hashlib.sha256(hashlib.sha256(address).digest()).digest()[:4]
# Build the raw address
address += checksum
# Encode the address in base58
encoded_address = b58encode(address)
print encoded_address
The encoding can be done with any base58 encoder, I used this. This should print out the address 17h6u26N2TVmoPRcvxwUdfAUjDzBJX513V
which is the address that sent the bitcoins in the transaction that we were looking at all the time. So you see, the information you were looking for was in the transaction all along, but quite hidden. So given an incoming transaction (or an outgoing as a matter of fact) you can reconstruct who the sender (or receiver) was. Notice that this allows you to reconstruct the sender of each individual input, which might be multiple addresses. Simply assuming that any of the addresses signing the inputs is the sender of the transaction may or may not lead to the desired semantic result.