Up to double spending attack in crypto

This commit is contained in:
Grant Sanderson 2017-07-04 11:51:04 -07:00
parent fb408ab80d
commit 9edccb116e

399
crypto.py
View file

@ -3520,18 +3520,393 @@ class TwoBlockChains(DistributedBlockChainScene):
self.randy = randy
return VGroup(randy)
class ReplaceCentralAuthorityWithWork(Scene):
def construct(self):
trust, central = words = TextMobject("Trust", "central authority")
words.scale(1.5)
cross = Cross(central)
work = TextMobject("computational work")
work.scale(1.5)
work.move_to(central, LEFT)
work.highlight(YELLOW)
self.play(Write(words))
self.play(ShowCreation(cross))
central.add(cross)
self.play(
central.shift, DOWN,
Write(work)
)
self.dither()
class AskAboutTrustingWork(TeacherStudentsScene):
def construct(self):
mode = "raise_left_hand"
self.student_says(
"Is trusting work \\\\ really enough?",
target_mode = mode,
)
self.change_student_modes("confused", mode, "erm")
self.dither(3)
class DoubleSpendingAttack(DistributedBlockChainScene):
CONFIG = {
"fraud_block_height" : 3,
}
def construct(self):
self.initialize_characters()
self.show_fraudulent_block()
self.send_to_bob()
self.dont_send_to_rest_of_network()
def initialize_characters(self):
network = self.get_large_network()
network.scale(0.7)
block_chains = self.get_distributed_ledgers()
self.add(network, block_chains)
self.play(self.alice.change, "conniving")
self.dither()
def show_fraudulent_block(self):
block = self.get_fraud_block()
block.next_to(self.alice, LEFT, LARGE_BUFF)
block.remove(block.content)
self.play(
ShowCreation(block),
Write(block.content),
self.alice.change, "raise_left_hand"
)
block.add(block.content)
self.dither()
self.block = block
def send_to_bob(self):
block = self.block.copy()
block.generate_target()
block.target.replace(
self.bob.block_chain.blocks[-1],
stretch = True
)
arrow = self.bob.block_chain.arrows[-1].copy()
VGroup(arrow, block.target).next_to(
self.bob.block_chain.blocks[-1], RIGHT, buff = 0
)
self.play(
MoveToTarget(block),
self.alice.change, "happy"
)
self.play(ShowCreation(arrow))
self.dither()
def dont_send_to_rest_of_network(self):
bubble = ThoughtBubble()
words = TextMobject("Alice", "never \\\\ paid", "Bob")
for name in "Alice", "Bob":
words.highlight_by_tex(name, self.get_color_from_name(name))
bubble.add_content(words)
bubble.resize_to_content()
bubble.add(*bubble.content)
bubble.move_to(self.you.get_corner(UP+RIGHT), DOWN+LEFT)
self.play(
self.charlie.change, "shruggie",
self.you.change, "shruggie",
)
self.play(LaggedStart(FadeIn, bubble))
self.play(self.bob.change, "confused", words)
self.dither(2)
###
def get_fraud_block(self):
block = self.get_block()
block.scale_to_fit_height(self.fraud_block_height)
content = VGroup()
tuples = [
("Prev hash", UP, BLUE),
("Proof of work", DOWN, GREEN),
]
for word, vect, color in tuples:
mob = TextMobject(word)
mob.highlight(color)
mob.scale_to_fit_height(0.07*block.get_height())
mob.next_to(
block.get_edge_center(vect), -vect,
buff = 0.06*block.get_height()
)
content.add(mob)
attr = word.lower().replace(" ", "_")
setattr(block, attr, mob)
payment = TextMobject("Alice", "pays", "Bob", "100 LD")
for name in "Alice", "Bob":
payment.highlight_by_tex(name, self.get_color_from_name(name))
payment.highlight_by_tex("LD", YELLOW)
payments = VGroup(
TexMobject("\\vdots"),
payment,
TexMobject("\\vdots")
)
payments.arrange_submobjects(DOWN)
payments.scale_to_fit_width(0.9*block.get_width())
payments.move_to(block)
content.add(payments)
block.content = content
block.add(content)
return block
class AliceRacesOtherMiners(DoubleSpendingAttack):
CONFIG = {
"n_frames_per_pow" : 3,
"fraud_block_height" : 2,
"n_miners" : 3,
"n_proof_of_work_digits" : 11,
"proof_of_work_update_counter" : 0,
}
def construct(self):
self.initialize_characters()
self.find_proof_of_work()
self.receive_broadcast_from_other_miners()
self.race_between_alice_and_miners()
def initialize_characters(self):
alice = self.alice
bob = self.bob
alice_l = VGroup(alice, alice.label)
bob_l = VGroup(bob, bob.label)
bob_l.move_to(ORIGIN)
alice_l.to_corner(UP+LEFT)
alice_l.shift(DOWN)
self.add(bob_l, alice_l)
chain = self.get_block_chain()
chain.next_to(bob, UP)
self.block_chain = chain
self.add(chain)
fraud_block = self.get_fraud_block()
fraud_block.next_to(alice, RIGHT)
fraud_block.to_edge(UP)
proof_of_work = self.get_rand_int_mob()
proof_of_work.replace(fraud_block.proof_of_work, dim_to_match = 1)
fraud_block.content.remove(fraud_block.proof_of_work)
fraud_block.content.add(proof_of_work)
fraud_block.proof_of_work = proof_of_work
self.fraud_block = fraud_block
self.add(fraud_block)
miners = VGroup(*[
PiCreature(color = GREY)
for x in range(self.n_miners)
])
miners.scale_to_fit_height(alice.get_height())
miners.arrange_submobjects(RIGHT, buff = LARGE_BUFF)
miners.to_edge(DOWN+LEFT)
miners.shift(0.5*UP)
miners_word = TextMobject("Miners")
miners_word.next_to(miners, DOWN)
self.miners = miners
self.add(miners_word, miners)
miner_blocks = VGroup()
self.proofs_of_work = VGroup()
self.add_foreground_mobject(self.proofs_of_work)
for miner in miners:
block = self.get_block()
block.scale_to_fit_width(1.5*miner.get_width())
block.next_to(miner, UP)
transactions = self.get_block_filler(block)
block.add(transactions)
proof_of_work = self.get_rand_int_mob()
prev_hash = TextMobject("Prev hash").highlight(BLUE)
for mob, vect in (proof_of_work, DOWN), (prev_hash, UP):
mob.scale_to_fit_height(0.1*block.get_height())
mob.next_to(
block.get_edge_center(vect), -vect,
buff = 0.05*block.get_height()
)
block.add(mob)
block.proof_of_work = proof_of_work
block.prev_hash = prev_hash
self.proofs_of_work.add(proof_of_work)
miner.block = block
miner_blocks.add(block)
self.add(miner_blocks)
def find_proof_of_work(self):
fraud_block = self.fraud_block
chain = self.block_chain
self.proofs_of_work.add(self.fraud_block.proof_of_work)
self.dither(3)
self.proofs_of_work.remove(self.fraud_block.proof_of_work)
fraud_block.proof_of_work.highlight(GREEN)
self.play(
Indicate(fraud_block.proof_of_work),
self.alice.change, "hooray"
)
self.dither()
block = fraud_block.copy()
block.generate_target()
block.target.replace(chain.blocks[-1], stretch = True)
arrow = chain.arrows[-1].copy()
VGroup(arrow, block.target).next_to(
chain.blocks[-1], RIGHT, buff = 0
)
self.remove(fraud_block)
self.clean_fraud_block_content()
self.fraud_fork_head = block
self.fraud_fork_arrow = arrow
self.play(MoveToTarget(block))
self.play(ShowCreation(arrow))
self.play(self.alice.change, "happy")
self.dither()
def receive_broadcast_from_other_miners(self):
winner = self.miners[-1]
proof_of_work = winner.block.proof_of_work
self.proofs_of_work.remove(proof_of_work)
proof_of_work.highlight(GREEN)
self.play(
Indicate(proof_of_work),
winner.change, "hooray"
)
block = winner.block.copy()
proof_of_work.highlight(WHITE)
self.remove(winner.block)
ff_head = self.fraud_fork_head
ff_arrow = self.fraud_fork_arrow
arrow = ff_arrow.copy()
movers = [ff_head, ff_arrow, block, arrow]
for mover in movers:
mover.generate_target()
block.target.replace(ff_head, stretch = True)
dist = 0.5*ff_head.get_height() + MED_SMALL_BUFF
block.target.shift(dist*DOWN)
ff_head.target.shift(dist*UP)
arrow.target[1].shift(dist*DOWN)
arrow.target.points[-2:] += dist*DOWN
ff_arrow.target[1].shift(dist*UP)
ff_arrow.target.points[-2:] += dist*UP
self.revert_to_original_skipping_status()
self.play(
Broadcast(block),
*[
MoveToTarget(
mover, run_time = 3,
rate_func = squish_rate_func(smooth, 0.3, 0.8)
)
for mover in movers
]
)
self.play(
FadeIn(winner.block),
winner.change_mode, "plain",
self.alice.change, "sassy",
)
self.proofs_of_work.add(winner.block.proof_of_work)
self.dither(2)
self.play(
self.alice.change, "pondering",
FadeIn(self.fraud_block)
)
self.proofs_of_work.add(self.fraud_block.proof_of_work)
self.valid_fork_head = block
def race_between_alice_and_miners(self):
last_fraud_block = self.fraud_fork_head
last_valid_block = self.valid_fork_head
chain = self.block_chain
winners = [
"Alice", "Alice",
"Miners", "Miners", "Miners",
"Alice", "Miners", "Miners", "Miners",
"Alice", "Miners", "Miners"
]
for winner in winners:
self.dither(2)
if winner == "Alice":
block = self.fraud_block
prev_block = last_fraud_block
else:
block = random.choice(self.miners).block
prev_block = last_valid_block
block_copy = block.deepcopy()
block_copy.proof_of_work.highlight(GREEN)
block_copy.generate_target()
block_copy.target.replace(chain.blocks[1], stretch = True)
arrow = chain.arrows[0].copy()
VGroup(arrow, block_copy.target).next_to(
prev_block, RIGHT, buff = 0
)
anims = [
MoveToTarget(block_copy, run_time = 2),
ShowCreation(
arrow,
run_time = 2,
rate_func = squish_rate_func(smooth, 0.5, 1),
),
FadeIn(block),
]
if winner == "Alice":
last_fraud_block = block_copy
else:
last_valid_block = block_copy
anims.append(Broadcast(block, run_time = 2))
self.play(*anims)
#####
def dither(self, time = 1):
self.play(
Animation(VGroup(*self.foreground_mobjects)),
run_time = time
)
def update_frame(self, *args, **kwargs):
self.update_proofs_of_work()
Scene.update_frame(self, *args, **kwargs)
def update_proofs_of_work(self):
self.proof_of_work_update_counter += 1
if self.proof_of_work_update_counter%self.n_frames_per_pow != 0:
return
for proof_of_work in self.proofs_of_work:
new_pow = self.get_rand_int_mob()
new_pow.replace(proof_of_work, dim_to_match = 1)
Transform(proof_of_work, new_pow).update(1)
def get_rand_int_mob(self):
e = self.n_proof_of_work_digits
return Integer(random.randint(10**e, 10**(e+1)))
def clean_fraud_block_content(self):
content = self.fraud_block.content
payments = content[1]
content.remove(payments)
transactions = self.get_block_filler(self.fraud_block)
content.add(transactions)
def get_block_filler(self, block):
result = TextMobject("$\\langle$Transactions$\\rangle$")
result.scale_to_fit_width(0.8*block.get_width())
result.move_to(block)
return result