mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 17:29:06 +08:00
Up to IntroduceBlockChain in crypto
This commit is contained in:
908
crypto.py
908
crypto.py
@ -28,6 +28,9 @@ from camera import Camera
|
||||
from mobject.svg_mobject import *
|
||||
from mobject.tex_mobject import *
|
||||
|
||||
from hashlib import sha256
|
||||
import binascii
|
||||
|
||||
#force_skipping
|
||||
#revert_to_original_skipping_status
|
||||
|
||||
@ -38,6 +41,30 @@ def get_cursive_name(name):
|
||||
result.set_stroke(width = 0.5)
|
||||
return result
|
||||
|
||||
def sha256_bit_string(message):
|
||||
hexdigest = sha256(message).hexdigest()
|
||||
return bin(int(hexdigest, 16))[2:]
|
||||
|
||||
def sha256_tex_mob(message, n_forced_start_zeros = 0):
|
||||
line = TexMobject("0"*32)
|
||||
pre_result = VGroup(*[
|
||||
line.copy() for row in range(8)
|
||||
])
|
||||
pre_result.arrange_submobjects(DOWN, buff = SMALL_BUFF)
|
||||
result = VGroup(*it.chain(*pre_result))
|
||||
result.scale(0.7)
|
||||
|
||||
true_bit_string = sha256_bit_string(message)
|
||||
n = n_forced_start_zeros
|
||||
bit_string = "0"*n + true_bit_string[n:]
|
||||
for i, (bit, part) in enumerate(zip(bit_string, result)):
|
||||
if bit == "1":
|
||||
one = TexMobject("1")[0]
|
||||
one.replace(part, dim_to_match = 1)
|
||||
result.submobjects[i] = one
|
||||
|
||||
return result
|
||||
|
||||
class TenDollarBill(VGroup):
|
||||
CONFIG = {
|
||||
"color" : GREEN,
|
||||
@ -348,6 +375,7 @@ class LedgerScene(PiCreatureScene):
|
||||
])
|
||||
labels = VGroup(*[pi.label for pi in creatures])
|
||||
self.network = VGroup(creatures, labels, lines)
|
||||
self.network.lines = lines
|
||||
return self.network
|
||||
|
||||
def create_pi_creatures(self):
|
||||
@ -2009,19 +2037,873 @@ class YouListeningToBroadcasts(LedgerScene):
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class AskWhatToAddToProtocol(InitialProtocol):
|
||||
def construct(self):
|
||||
self.add_title()
|
||||
items = VGroup(*map(self.get_new_item, [
|
||||
"Broadcast transactions",
|
||||
"Only accept signed transactions",
|
||||
"No overspending",
|
||||
] + [""]*6))
|
||||
brace = Brace(VGroup(*items[3:]), LEFT)
|
||||
question = TextMobject("What to \\\\ add here?")
|
||||
question.highlight(RED)
|
||||
question.scale(1.5)
|
||||
brace.highlight(RED)
|
||||
question.next_to(brace, LEFT)
|
||||
|
||||
self.add(*items[:3])
|
||||
self.play(GrowFromCenter(brace))
|
||||
self.play(Write(question))
|
||||
self.dither()
|
||||
|
||||
class TrustComputationalWork(DistributedLedgerScene):
|
||||
def construct(self):
|
||||
self.add_ledger()
|
||||
self.show_work()
|
||||
|
||||
def add_ledger(self):
|
||||
ledgers = self.get_distributed_ledgers()
|
||||
ledger = ledgers[0]
|
||||
ledger.scale(3)
|
||||
ledger[1].scale_in_place(2./3)
|
||||
ledger.center().to_edge(UP).shift(4*LEFT)
|
||||
plus = TexMobject("+")
|
||||
plus.next_to(ledger, RIGHT)
|
||||
|
||||
self.add(ledger, plus)
|
||||
self.ledger = ledger
|
||||
self.plus = plus
|
||||
|
||||
def show_work(self):
|
||||
zeros = TexMobject("0"*32)
|
||||
zeros.next_to(self.plus, RIGHT)
|
||||
brace = Brace(zeros, DOWN)
|
||||
words = brace.get_text("Computational work")
|
||||
self.add(brace, words)
|
||||
|
||||
for n in range(2**12):
|
||||
binary = bin(n)[2:]
|
||||
for i, bit_str in enumerate(reversed(binary)):
|
||||
curr_bit = zeros.submobjects[-i-1]
|
||||
new_bit = TexMobject(bit_str)
|
||||
new_bit.replace(curr_bit, dim_to_match = 1)
|
||||
if bit_str == "1":
|
||||
new_bit.highlight(YELLOW)
|
||||
zeros.submobjects[-i-1] = new_bit
|
||||
self.remove(curr_bit)
|
||||
self.add(zeros)
|
||||
self.dither(1./30)
|
||||
|
||||
class TrustComputationalWorkSupplement(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject(
|
||||
"Main tool: ", "Cryptographic hash functions"
|
||||
)
|
||||
words[1].highlight(YELLOW)
|
||||
self.add(words[0])
|
||||
self.play(Write(words[1]))
|
||||
self.dither()
|
||||
|
||||
class ThisIsWellIntoTheWeeds(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
idea = TextMobject("Proof of work")
|
||||
idea.move_to(self.teacher.get_corner(UP+LEFT))
|
||||
idea.shift(MED_LARGE_BUFF*UP)
|
||||
idea.save_state()
|
||||
lightbulb = Lightbulb()
|
||||
lightbulb.next_to(idea, UP)
|
||||
idea.shift(DOWN)
|
||||
idea.set_fill(opacity = 0)
|
||||
|
||||
self.teacher_says(
|
||||
"We're well into \\\\ the weeds now",
|
||||
target_mode = "sassy",
|
||||
added_anims = [
|
||||
ApplyMethod(pi.change, mode)
|
||||
for pi, mode in zip(self.students, [
|
||||
"hooray", "sad", "erm"
|
||||
])
|
||||
],
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
idea.restore,
|
||||
RemovePiCreatureBubble(
|
||||
self.teacher, target_mode = "hooray",
|
||||
look_at_arg = lightbulb
|
||||
),
|
||||
)
|
||||
self.change_student_modes(
|
||||
*["pondering"]*3,
|
||||
added_anims = [LaggedStart(FadeIn, lightbulb)]
|
||||
)
|
||||
self.play(LaggedStart(
|
||||
ApplyMethod, lightbulb,
|
||||
lambda b : (b.highlight, YELLOW_A),
|
||||
rate_func = there_and_back
|
||||
))
|
||||
self.dither(2)
|
||||
|
||||
class IntroduceSHA256(Scene):
|
||||
def construct(self):
|
||||
self.introduce_evaluation()
|
||||
self.inverse_function_question()
|
||||
self.issue_challenge()
|
||||
self.shift_everything_down()
|
||||
self.guess_and_check()
|
||||
|
||||
def introduce_evaluation(self):
|
||||
messages = [
|
||||
"3Blue1Brown",
|
||||
"3Blue1Crown",
|
||||
"Mathologer",
|
||||
"Infinite Series",
|
||||
"Numberphile",
|
||||
"Welch Labs",
|
||||
"3Blue1Brown",
|
||||
]
|
||||
groups = VGroup()
|
||||
for message in messages:
|
||||
lhs = TextMobject(
|
||||
"SHA256", "(``", message, "'') =",
|
||||
arg_separator = ""
|
||||
)
|
||||
lhs.highlight_by_tex(message, BLUE)
|
||||
digest = sha256_tex_mob(message)
|
||||
digest.next_to(lhs, RIGHT)
|
||||
group = VGroup(lhs, digest)
|
||||
group.to_corner(UP+RIGHT)
|
||||
group.shift(MED_LARGE_BUFF*DOWN)
|
||||
groups.add(group)
|
||||
|
||||
group = groups[0]
|
||||
lhs, digest = group
|
||||
sha, lp, message, lp = lhs
|
||||
sha_brace = Brace(sha, UP)
|
||||
message_brace = Brace(message, DOWN)
|
||||
digest_brace = Brace(digest, DOWN)
|
||||
sha_text = sha_brace.get_text("", "Hash function")
|
||||
sha_text.highlight(YELLOW)
|
||||
message_text = message_brace.get_text("Message/file")
|
||||
message_text.highlight(BLUE)
|
||||
digest_text = digest_brace.get_text("``Hash'' or ``Digest''")
|
||||
brace_text_pairs = [
|
||||
(sha_brace, sha_text),
|
||||
(message_brace, message_text),
|
||||
(digest_brace, digest_text),
|
||||
]
|
||||
|
||||
self.add(group)
|
||||
for brace, text in brace_text_pairs:
|
||||
self.play(
|
||||
GrowFromCenter(brace),
|
||||
Write(text, run_time = 2)
|
||||
)
|
||||
self.dither()
|
||||
self.dither()
|
||||
for mob in digest, message:
|
||||
self.play(LaggedStart(
|
||||
ApplyMethod, mob,
|
||||
lambda m : (m.highlight, YELLOW),
|
||||
rate_func = there_and_back,
|
||||
run_time = 1
|
||||
))
|
||||
self.dither()
|
||||
|
||||
new_lhs, new_digest = groups[1]
|
||||
char = new_lhs[2][-5]
|
||||
arrow = Arrow(UP, ORIGIN, buff = 0)
|
||||
arrow.next_to(char, UP)
|
||||
arrow.highlight(RED)
|
||||
self.play(ShowCreation(arrow))
|
||||
for new_group in groups[1:]:
|
||||
new_lhs, new_digest = new_group
|
||||
new_message = new_lhs[2]
|
||||
self.play(
|
||||
Transform(lhs, new_lhs),
|
||||
message_brace.stretch_to_fit_width, new_message.get_width(),
|
||||
message_brace.next_to, new_message, DOWN,
|
||||
MaintainPositionRelativeTo(message_text, message_brace),
|
||||
MaintainPositionRelativeTo(sha_brace, lhs[0]),
|
||||
MaintainPositionRelativeTo(sha_text, sha_brace)
|
||||
)
|
||||
self.play(Transform(
|
||||
digest, new_digest,
|
||||
run_time = 2,
|
||||
submobject_mode = "lagged_start",
|
||||
path_arc = np.pi/2
|
||||
))
|
||||
if arrow in self.get_mobjects():
|
||||
self.dither()
|
||||
self.play(FadeOut(arrow))
|
||||
self.dither()
|
||||
|
||||
new_sha_text = TextMobject(
|
||||
"Cryptographic", "hash function"
|
||||
)
|
||||
new_sha_text.next_to(sha_brace, UP)
|
||||
new_sha_text.shift_onto_screen()
|
||||
new_sha_text.highlight(YELLOW)
|
||||
new_sha_text[0].highlight(GREEN)
|
||||
self.play(Transform(sha_text, new_sha_text))
|
||||
self.dither()
|
||||
|
||||
self.lhs = lhs
|
||||
self.message = message
|
||||
self.digest = digest
|
||||
self.digest_text = digest_text
|
||||
self.message_text = message_text
|
||||
|
||||
def inverse_function_question(self):
|
||||
arrow = Arrow(3*RIGHT, 3*LEFT, buff = 0)
|
||||
arrow.set_stroke(width = 8)
|
||||
arrow.highlight(RED)
|
||||
everything = VGroup(*self.get_mobjects())
|
||||
arrow.next_to(everything, DOWN)
|
||||
words = TextMobject("Inverse is infeasible")
|
||||
words.highlight(RED)
|
||||
words.next_to(arrow, DOWN)
|
||||
|
||||
self.play(ShowCreation(arrow))
|
||||
self.play(Write(words))
|
||||
self.dither()
|
||||
|
||||
def issue_challenge(self):
|
||||
desired_output_text = TextMobject("Desired output")
|
||||
desired_output_text.move_to(self.digest_text)
|
||||
desired_output_text.highlight(YELLOW)
|
||||
new_digest = sha256_tex_mob("Challenge")
|
||||
new_digest.replace(self.digest)
|
||||
q_marks = TextMobject("???")
|
||||
q_marks.move_to(self.message_text)
|
||||
q_marks.highlight(BLUE)
|
||||
|
||||
self.play(
|
||||
Transform(
|
||||
self.digest, new_digest,
|
||||
run_time = 2,
|
||||
submobject_mode = "lagged_start",
|
||||
path_arc = np.pi/2
|
||||
),
|
||||
Transform(self.digest_text, desired_output_text)
|
||||
)
|
||||
self.play(
|
||||
FadeOut(self.message),
|
||||
Transform(self.message_text, q_marks)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
def shift_everything_down(self):
|
||||
everything = VGroup(*self.get_top_level_mobjects())
|
||||
self.play(
|
||||
everything.scale, 0.85,
|
||||
everything.to_edge, DOWN
|
||||
)
|
||||
|
||||
def guess_and_check(self):
|
||||
groups = VGroup()
|
||||
for x in range(32):
|
||||
message = "Guess \\#%d"%x
|
||||
lhs = TextMobject(
|
||||
"SHA256(``", message, "'') = ",
|
||||
arg_separator = ""
|
||||
)
|
||||
lhs.highlight_by_tex("Guess", BLUE)
|
||||
digest = sha256_tex_mob(message)
|
||||
digest.next_to(lhs, RIGHT)
|
||||
group = VGroup(lhs, digest)
|
||||
group.scale(0.85)
|
||||
group.next_to(self.digest, UP, aligned_edge = RIGHT)
|
||||
group.to_edge(UP)
|
||||
groups.add(group)
|
||||
|
||||
group = groups[0]
|
||||
self.play(FadeIn(group))
|
||||
for new_group in groups[1:]:
|
||||
self.play(Transform(
|
||||
group[0], new_group[0],
|
||||
run_time = 0.5,
|
||||
))
|
||||
self.play(Transform(
|
||||
group[1], new_group[1],
|
||||
run_time = 1,
|
||||
submobject_mode = "lagged_start"
|
||||
))
|
||||
|
||||
class PonderScematic(Scene):
|
||||
def construct(self):
|
||||
randy = Randolph()
|
||||
randy.to_corner(DOWN+LEFT)
|
||||
self.play(randy.change, "confused", ORIGIN)
|
||||
for x in range(3):
|
||||
self.play(Blink(randy))
|
||||
self.dither(2)
|
||||
|
||||
class ViewingSLLCertificate(ExternallyAnimatedScene):
|
||||
pass
|
||||
|
||||
class SHA256ToProofOfWork(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
sha = TextMobject("SHA256")
|
||||
proof = TextMobject("Proof of work")
|
||||
arrow = Arrow(LEFT, RIGHT)
|
||||
group = VGroup(sha, arrow, proof)
|
||||
group.arrange_submobjects(RIGHT)
|
||||
group.next_to(self.teacher, UP, buff = LARGE_BUFF)
|
||||
group.to_edge(RIGHT, buff = LARGE_BUFF)
|
||||
|
||||
self.play(
|
||||
Write(sha, run_time = 1),
|
||||
self.teacher.change, "raise_right_hand"
|
||||
)
|
||||
self.play(ShowCreation(arrow))
|
||||
self.play(Write(proof, run_time = 1))
|
||||
self.dither(3)
|
||||
|
||||
class IntroduceNonceOnTrasactions(LedgerScene):
|
||||
CONFIG = {
|
||||
"denomination" : "LD",
|
||||
"ledger_width" : 5,
|
||||
"ledger_line_height" : 0.3,
|
||||
}
|
||||
def construct(self):
|
||||
self.add(self.get_ledger())
|
||||
self.hash_with_nonce()
|
||||
self.write_probability()
|
||||
self.guess_and_check()
|
||||
self.name_proof_of_work()
|
||||
self.change_ledger()
|
||||
self.guess_and_check()
|
||||
|
||||
def hash_with_nonce(self):
|
||||
ledger = self.ledger
|
||||
self.add(*[
|
||||
self.add_payment_line_to_ledger(*payment)
|
||||
for payment in [
|
||||
("Alice", "Bob", 20),
|
||||
("Alice", "You", 30),
|
||||
("Charlie", "You", 100),
|
||||
]
|
||||
])
|
||||
|
||||
nonce = TexMobject(str(2**30 + hash("Hey there")%(2**15)))
|
||||
nonce.next_to(ledger, RIGHT, LARGE_BUFF)
|
||||
nonce.highlight(GREEN_C)
|
||||
nonce_brace = Brace(nonce, DOWN)
|
||||
special_word = nonce_brace.get_text("Special number")
|
||||
arrow = Arrow(LEFT, RIGHT, buff = 0)
|
||||
arrow.next_to(ledger, RIGHT)
|
||||
arrow.shift(MED_LARGE_BUFF*DOWN)
|
||||
sha = TextMobject("SHA256")
|
||||
sha.next_to(arrow, UP)
|
||||
digest = sha256_tex_mob(
|
||||
"""Man, you're reading this deeply into
|
||||
the code behind videos? I'm touched,
|
||||
really touched. Keeping loving math, my
|
||||
friend. """,
|
||||
n_forced_start_zeros = 30,
|
||||
)
|
||||
digest.next_to(arrow, RIGHT)
|
||||
zeros = VGroup(*digest[:30])
|
||||
zeros_brace = Brace(zeros, UP)
|
||||
zeros_words = zeros_brace.get_text("30 zeros")
|
||||
|
||||
self.play(LaggedStart(
|
||||
FadeIn, VGroup(special_word, nonce_brace, nonce)
|
||||
))
|
||||
self.dither()
|
||||
self.play(
|
||||
nonce.next_to, ledger.content, DOWN, MED_SMALL_BUFF, LEFT,
|
||||
FadeOut(special_word),
|
||||
FadeOut(nonce_brace)
|
||||
)
|
||||
ledger.content.add(nonce)
|
||||
decomposed_ledger = VGroup(*filter(
|
||||
lambda m : not m.is_subpath,
|
||||
ledger.family_members_with_points()
|
||||
))
|
||||
self.play(
|
||||
ShowCreation(arrow),
|
||||
FadeIn(sha)
|
||||
)
|
||||
self.play(LaggedStart(
|
||||
ApplyMethod, decomposed_ledger,
|
||||
lambda m : (m.highlight, YELLOW),
|
||||
rate_func = there_and_back
|
||||
))
|
||||
point = VectorizedPoint(sha.get_center())
|
||||
point.set_fill(opacity = 1)
|
||||
self.play(LaggedStart(
|
||||
Transform, decomposed_ledger.copy(),
|
||||
lambda m : (m, point),
|
||||
run_time = 1
|
||||
))
|
||||
bit_iter = iter(digest)
|
||||
self.play(LaggedStart(
|
||||
ReplacementTransform,
|
||||
VGroup(*[point.copy() for x in range(256)]),
|
||||
lambda m : (m, bit_iter.next()),
|
||||
))
|
||||
self.remove(*self.get_mobjects_from_last_animation())
|
||||
self.add(digest)
|
||||
self.play(
|
||||
GrowFromCenter(zeros_brace),
|
||||
Write(zeros_words, run_time = 1)
|
||||
)
|
||||
self.play(LaggedStart(
|
||||
ApplyMethod, zeros,
|
||||
lambda m : (m.highlight, YELLOW)
|
||||
))
|
||||
self.dither(2)
|
||||
|
||||
self.nonce = nonce
|
||||
self.digest = digest
|
||||
self.zeros_brace = zeros_brace
|
||||
self.zeros_words = zeros_words
|
||||
|
||||
def write_probability(self):
|
||||
probability = TextMobject(
|
||||
"Probability: $\\frac{1}{2^{30}}$",
|
||||
"$\\approx \\frac{1}{1{,}000{,}000{,}000}$",
|
||||
)
|
||||
probability.next_to(self.zeros_words, UP, MED_LARGE_BUFF)
|
||||
|
||||
self.play(FadeIn(probability[0]))
|
||||
self.dither()
|
||||
self.play(Write(probability[1], run_time = 2))
|
||||
self.dither(2)
|
||||
|
||||
def guess_and_check(self):
|
||||
q_mark = TexMobject("?")
|
||||
q_mark.highlight(RED)
|
||||
q_mark.next_to(self.zeros_words, RIGHT, SMALL_BUFF)
|
||||
|
||||
self.digest.save_state()
|
||||
self.nonce.save_state()
|
||||
|
||||
self.play(FadeIn(q_mark))
|
||||
for x in range(1, 13):
|
||||
nonce = TexMobject(str(x))
|
||||
nonce.move_to(self.nonce)
|
||||
nonce.highlight(GREEN_C)
|
||||
digest = sha256_tex_mob(str(x))
|
||||
digest.replace(self.digest)
|
||||
|
||||
self.play(Transform(
|
||||
self.nonce, nonce,
|
||||
run_time = 1 if x == 1 else 0.3
|
||||
))
|
||||
self.play(Transform(
|
||||
self.digest, digest,
|
||||
run_time = 1,
|
||||
submobject_mode = "lagged_start"
|
||||
))
|
||||
self.dither()
|
||||
self.play(self.nonce.restore)
|
||||
self.play(
|
||||
self.digest.restore,
|
||||
submobject_mode = "lagged_start",
|
||||
run_time = 2
|
||||
)
|
||||
self.play(FadeOut(q_mark))
|
||||
self.dither()
|
||||
|
||||
def name_proof_of_work(self):
|
||||
words = TextMobject("``Proof of work''")
|
||||
words.next_to(self.nonce, DOWN, LARGE_BUFF)
|
||||
words.shift(MED_LARGE_BUFF*RIGHT)
|
||||
words.highlight(GREEN)
|
||||
arrow = Arrow(
|
||||
words.get_top(), self.nonce.get_bottom(),
|
||||
color = WHITE,
|
||||
tip_length = 0.15
|
||||
)
|
||||
self.play(Write(words, run_time = 2))
|
||||
self.play(ShowCreation(arrow))
|
||||
self.dither()
|
||||
|
||||
def change_ledger(self):
|
||||
amount = self.ledger.content[2][-1]
|
||||
new_amount = TextMobject("300 LD")
|
||||
new_amount.scale_to_fit_height(amount.get_height())
|
||||
new_amount.highlight(amount.get_color())
|
||||
new_amount.move_to(amount, LEFT)
|
||||
|
||||
new_digest = sha256_tex_mob("Ah shucks")
|
||||
new_digest.replace(self.digest)
|
||||
|
||||
dot = Dot(amount.get_center())
|
||||
dot.set_fill(opacity = 0.5)
|
||||
|
||||
self.play(FocusOn(amount))
|
||||
self.play(Transform(amount, new_amount))
|
||||
self.play(
|
||||
dot.move_to, new_digest,
|
||||
dot.set_fill, None, 0
|
||||
)
|
||||
self.play(Transform(
|
||||
self.digest, new_digest,
|
||||
submobject_mode = "lagged_start",
|
||||
))
|
||||
|
||||
class ShowSomeBroadcasting(DistributedLedgerScene):
|
||||
def construct(self):
|
||||
self.add_large_network_and_distributed_ledger()
|
||||
lines = self.network.lines.copy()
|
||||
lines.add(*[
|
||||
line.copy().rotate(np.pi)
|
||||
for line in lines
|
||||
])
|
||||
|
||||
point = VectorizedPoint(self.pi_creatures.get_center())
|
||||
last_pi = None
|
||||
for pi in self.pi_creatures:
|
||||
outgoing_lines = []
|
||||
for line in lines:
|
||||
vect = line.get_start() - pi.get_center()
|
||||
dist = np.linalg.norm(vect)
|
||||
if dist < 2:
|
||||
outgoing_lines.append(line)
|
||||
dots = VGroup()
|
||||
for line in outgoing_lines:
|
||||
dot = Dot(line.get_start())
|
||||
dot.highlight(YELLOW)
|
||||
dot.generate_target()
|
||||
dot.target.move_to(line.get_end())
|
||||
for alt_pi in self.pi_creatures:
|
||||
vect = line.get_end() - alt_pi.get_center()
|
||||
dist = np.linalg.norm(vect)
|
||||
if dist < 2:
|
||||
dot.ledger = alt_pi.ledger
|
||||
dots.add(dot)
|
||||
self.play(
|
||||
Animation(point),
|
||||
Broadcast(pi),
|
||||
*[
|
||||
Succession(
|
||||
FadeIn(dot),
|
||||
MoveToTarget(dot, run_time = 2),
|
||||
)
|
||||
for dot in dots
|
||||
]
|
||||
)
|
||||
self.play(*it.chain(*[
|
||||
[dot.move_to, dot.ledger, dot.set_fill, None, 0]
|
||||
for dot in dots
|
||||
]))
|
||||
|
||||
class IntroduceBlockChain(Scene):
|
||||
CONFIG = {
|
||||
"transaction_color" : YELLOW,
|
||||
"proof_of_work_color" : GREEN,
|
||||
"prev_hash_color" : BLUE,
|
||||
"block_width" : 3,
|
||||
"block_height" : 3.5,
|
||||
"n_transaction_lines" : 8,
|
||||
"payment_height_to_block_height" : 0.15,
|
||||
}
|
||||
def setup(self):
|
||||
ls = LedgerScene()
|
||||
self.names = [
|
||||
name.capitalize()
|
||||
for name in ls.get_names()
|
||||
]
|
||||
self.name_colors = [
|
||||
ls.get_color_from_name(name)
|
||||
for name in self.names
|
||||
]
|
||||
|
||||
def construct(self):
|
||||
self.divide_ledger_into_blocks()
|
||||
self.show_proofs_of_work()
|
||||
self.chain_blocks_together()
|
||||
self.mess_with_early_block()
|
||||
self.propogate_hash_change()
|
||||
self.redo_proof_of_work()
|
||||
self.write_block_chain()
|
||||
|
||||
|
||||
def divide_ledger_into_blocks(self):
|
||||
blocks = VGroup(*[
|
||||
self.get_block() for x in range(3)
|
||||
])
|
||||
blocks.arrange_submobjects(RIGHT, buff = 1.5)
|
||||
blocks.to_edge(UP)
|
||||
|
||||
all_payments = VGroup()
|
||||
all_proofs_of_work = VGroup()
|
||||
for block in blocks:
|
||||
block.remove(block.prev_hash)
|
||||
all_payments.add(*block.payments)
|
||||
all_proofs_of_work.add(block.proof_of_work)
|
||||
|
||||
blocks_word = TextMobject("Blocks")
|
||||
blocks_word.scale(1.5)
|
||||
blocks_word.shift(2*DOWN)
|
||||
arrows = VGroup(*[
|
||||
Arrow(
|
||||
blocks_word.get_top(), block.get_bottom(),
|
||||
buff = MED_LARGE_BUFF,
|
||||
color = WHITE
|
||||
)
|
||||
for block in blocks
|
||||
])
|
||||
|
||||
self.play(LaggedStart(FadeIn, blocks))
|
||||
self.play(
|
||||
Write(blocks_word),
|
||||
LaggedStart(
|
||||
ShowCreation, arrows,
|
||||
run_time = 1,
|
||||
lag_factor = 0.6,
|
||||
)
|
||||
)
|
||||
self.dither()
|
||||
for group in all_payments, all_proofs_of_work:
|
||||
self.play(LaggedStart(
|
||||
Indicate, group,
|
||||
rate_func = there_and_back,
|
||||
scale_factor = 1.1,
|
||||
))
|
||||
self.play(*map(FadeOut, [blocks_word, arrows]))
|
||||
|
||||
self.blocks = blocks
|
||||
|
||||
def show_proofs_of_work(self):
|
||||
random.seed(0)
|
||||
blocks = self.blocks
|
||||
|
||||
proofs_of_work = VGroup()
|
||||
new_proofs_of_work = VGroup()
|
||||
digests = VGroup()
|
||||
arrows = VGroup()
|
||||
sha_words = VGroup()
|
||||
signatures = VGroup()
|
||||
|
||||
for block in blocks:
|
||||
proofs_of_work.add(block.proof_of_work)
|
||||
num_str = str(random.randint(0, 10**12))
|
||||
number = TexMobject(num_str)
|
||||
number.highlight(self.proof_of_work_color)
|
||||
number.replace(block.proof_of_work, dim_to_match = 1)
|
||||
new_proofs_of_work.add(number)
|
||||
|
||||
digest = sha256_tex_mob(num_str, 60)
|
||||
digest.scale(0.7)
|
||||
digest.move_to(block).to_edge(DOWN)
|
||||
VGroup(*digest[:60]).highlight(YELLOW)
|
||||
arrow = Arrow(block, digest)
|
||||
sha = TextMobject("SHA256")
|
||||
sha.scale(0.7)
|
||||
point = arrow.get_center()
|
||||
sha.next_to(point, UP, SMALL_BUFF)
|
||||
sha.rotate(-np.pi/2, about_point = point)
|
||||
sha.shift(SMALL_BUFF*UP)
|
||||
digests.add(digest)
|
||||
arrows.add(arrow)
|
||||
sha_words.add(sha)
|
||||
|
||||
for payment in block.payments[:2]:
|
||||
signatures.add(payment[-1])
|
||||
|
||||
proofs_of_work.save_state()
|
||||
|
||||
self.play(Transform(
|
||||
proofs_of_work, new_proofs_of_work,
|
||||
submobject_mode = "lagged_start"
|
||||
))
|
||||
self.play(
|
||||
ShowCreation(arrows),
|
||||
Write(sha_words),
|
||||
run_time = 2
|
||||
)
|
||||
self.play(Write(digests))
|
||||
self.dither()
|
||||
for group in signatures, proofs_of_work:
|
||||
self.play(LaggedStart(
|
||||
Indicate, group,
|
||||
run_time = 2,
|
||||
rate_func = there_and_back,
|
||||
))
|
||||
self.dither()
|
||||
self.play(
|
||||
proofs_of_work.restore,
|
||||
FadeOut(sha_words)
|
||||
)
|
||||
|
||||
self.digests = digests
|
||||
self.sha_arrows = arrows
|
||||
|
||||
def chain_blocks_together(self):
|
||||
blocks = self.blocks
|
||||
digests = self.digests
|
||||
sha_arrows = self.sha_arrows
|
||||
block_spacing = blocks[1].get_center() - blocks[0].get_center()
|
||||
prev_hashes = VGroup(*[
|
||||
block.prev_hash for block in blocks
|
||||
])
|
||||
|
||||
prev_hashes.add(
|
||||
prev_hashes[-1].copy().shift(block_spacing).fade(1)
|
||||
)
|
||||
|
||||
new_arrows = VGroup()
|
||||
for block in blocks:
|
||||
end = np.array([
|
||||
block.get_left()[0] + block_spacing[0],
|
||||
block.prev_hash.get_center()[1],
|
||||
0
|
||||
])
|
||||
arrow = Arrow(end+LEFT, end, buff = SMALL_BUFF)
|
||||
arrow.points[0] = block.get_right()
|
||||
arrow.points[1] = block.get_right() + RIGHT
|
||||
arrow.points[2] = end + LEFT + SMALL_BUFF*UP
|
||||
new_arrows.add(arrow)
|
||||
|
||||
for i in range(3):
|
||||
self.play(
|
||||
ReplacementTransform(digests[i], prev_hashes[i+1]),
|
||||
Transform(sha_arrows[i], new_arrows[i])
|
||||
)
|
||||
arrow = new_arrows[0].copy().shift(-block_spacing)
|
||||
sha_arrows.add_to_back(arrow)
|
||||
self.play(*map(FadeIn, [arrow, prev_hashes[0]]))
|
||||
self.dither(2)
|
||||
|
||||
self.prev_hashes = prev_hashes
|
||||
|
||||
def mess_with_early_block(self):
|
||||
blocks = self.blocks
|
||||
amount = blocks[0].payments[1][3]
|
||||
new_amount = TextMobject("400 LD")
|
||||
new_amount.scale_to_fit_height(amount.get_height())
|
||||
new_amount.highlight(RED)
|
||||
new_amount.move_to(amount, LEFT)
|
||||
|
||||
self.play(FocusOn(amount))
|
||||
self.play(Transform(amount, new_amount))
|
||||
self.dither()
|
||||
self.play(Swap(*blocks[:2]))
|
||||
self.dither()
|
||||
|
||||
blocks.submobjects[:2] = blocks.submobjects[1::-1]
|
||||
|
||||
def propogate_hash_change(self):
|
||||
alt_prev_hashes = self.prev_hashes.copy()
|
||||
alt_prev_hashes.highlight(RED)
|
||||
|
||||
for block, prev_hash in zip(self.blocks, alt_prev_hashes[1:]):
|
||||
rect = block.rect.copy()
|
||||
rect.set_stroke(RED, 8)
|
||||
self.play(ShowCreation(rect))
|
||||
self.play(ReplacementTransform(rect, prev_hash))
|
||||
self.alt_prev_hashes = alt_prev_hashes
|
||||
|
||||
def redo_proof_of_work(self):
|
||||
proofs_of_work = VGroup(*[
|
||||
block.proof_of_work for block in self.blocks
|
||||
])
|
||||
hashes = self.alt_prev_hashes[1:]
|
||||
|
||||
self.play(FadeOut(proofs_of_work))
|
||||
for proof_of_work, prev_hash in zip(proofs_of_work, hashes):
|
||||
num_pow_group = VGroup(*[
|
||||
Integer(random.randint(10**9, 10**10))
|
||||
for x in range(50)
|
||||
])
|
||||
num_pow_group.highlight(proof_of_work.get_color())
|
||||
num_pow_group.scale_to_fit_width(proof_of_work.get_width())
|
||||
num_pow_group.move_to(proof_of_work)
|
||||
for num_pow in num_pow_group:
|
||||
self.add(num_pow)
|
||||
self.dither(1./20)
|
||||
self.remove(num_pow)
|
||||
self.add(num_pow)
|
||||
prev_hash.highlight(BLUE)
|
||||
|
||||
def write_block_chain(self):
|
||||
ledger = TextMobject("Ledger")
|
||||
ledger.next_to(self.blocks, DOWN)
|
||||
cross = Cross(ledger)
|
||||
block_chain = TextMobject("``Block Chain''")
|
||||
block_chain.next_to(ledger, DOWN)
|
||||
|
||||
self.play(FadeIn(ledger))
|
||||
self.play(
|
||||
ShowCreation(cross),
|
||||
Write(block_chain)
|
||||
)
|
||||
self.dither(2)
|
||||
|
||||
|
||||
######
|
||||
|
||||
def get_block(self):
|
||||
block = VGroup()
|
||||
rect = Rectangle(
|
||||
color = WHITE,
|
||||
height = self.block_height,
|
||||
width = self.block_width,
|
||||
)
|
||||
h_line1, h_line2 = [
|
||||
Line(
|
||||
rect.get_left(), rect.get_right()
|
||||
).shift(0.3*rect.get_height()*vect)
|
||||
for vect in UP, DOWN
|
||||
]
|
||||
|
||||
payments = VGroup()
|
||||
if not hasattr(self, "transaction_counter"):
|
||||
self.transaction_counter = 0
|
||||
for x in range(2):
|
||||
hashes = [
|
||||
hash("%d %d"%(seed, self.transaction_counter))
|
||||
for seed in range(3)
|
||||
]
|
||||
payment = TextMobject(
|
||||
self.names[hashes[0]%3],
|
||||
"pays",
|
||||
self.names[hashes[1]%4],
|
||||
"%d0 LD"%(hashes[2]%9 + 1),
|
||||
)
|
||||
payment.highlight_by_tex("LD", YELLOW)
|
||||
for name, color in zip(self.names, self.name_colors):
|
||||
payment.highlight_by_tex(name, color)
|
||||
signature = TextMobject("$\\langle$ Signature $\\rangle$")
|
||||
signature.highlight(payment[0].get_color())
|
||||
signature.next_to(payment, DOWN, SMALL_BUFF)
|
||||
payment.add(signature)
|
||||
|
||||
factor = self.payment_height_to_block_height
|
||||
payment.scale_to_fit_height(factor*rect.get_height())
|
||||
payments.add(payment)
|
||||
self.transaction_counter += 1
|
||||
payments.add(TexMobject("\\dots").scale(0.5))
|
||||
payments.arrange_submobjects(DOWN, buff = MED_SMALL_BUFF)
|
||||
payments.next_to(h_line1, DOWN)
|
||||
|
||||
proof_of_work = TextMobject("Proof of work")
|
||||
proof_of_work.highlight(self.proof_of_work_color)
|
||||
proof_of_work.scale(0.8)
|
||||
proof_of_work.move_to(
|
||||
VGroup(h_line2, VectorizedPoint(rect.get_bottom()))
|
||||
)
|
||||
|
||||
prev_hash = TextMobject("Prev hash")
|
||||
prev_hash.scale(0.8)
|
||||
prev_hash.highlight(self.prev_hash_color)
|
||||
prev_hash.move_to(
|
||||
VGroup(h_line1, VectorizedPoint(rect.get_top()))
|
||||
)
|
||||
|
||||
block.rect = rect
|
||||
block.h_lines = VGroup(h_line1, h_line2)
|
||||
block.payments = payments
|
||||
block.proof_of_work = proof_of_work
|
||||
block.prev_hash = prev_hash
|
||||
block.digest_mobject_attrs()
|
||||
return block
|
||||
|
||||
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
\begin{document}
|
||||
\centering
|
||||
|
||||
$\displaystyle YourTextHere$
|
||||
\begin{align*}
|
||||
YourTextHere
|
||||
\end{align*}
|
||||
|
||||
\end{document}
|
@ -563,7 +563,7 @@ class PiCreatureScene(Scene):
|
||||
return self
|
||||
|
||||
def dither(self, time = 1, blink = True):
|
||||
while time > 0:
|
||||
while time >= 1:
|
||||
time_to_blink = self.total_dither_time%self.seconds_to_blink == 0
|
||||
if blink and self.any_pi_creatures_on_screen() and time_to_blink:
|
||||
self.blink()
|
||||
@ -571,6 +571,8 @@ class PiCreatureScene(Scene):
|
||||
Scene.dither(self)
|
||||
time -= 1
|
||||
self.total_dither_time += 1
|
||||
if time > 0:
|
||||
Scene.dither(self, time)
|
||||
return self
|
||||
|
||||
def change_mode(self, mode):
|
||||
|
@ -265,22 +265,6 @@ class DoubleArrow(Arrow):
|
||||
Arrow.__init__(self, *args, **kwargs)
|
||||
self.add_tip(add_at_end = False)
|
||||
|
||||
|
||||
class Cross(VMobject):
|
||||
CONFIG = {
|
||||
"color" : YELLOW,
|
||||
"radius" : 0.3
|
||||
}
|
||||
def generate_points(self):
|
||||
p1, p2, p3, p4 = self.radius * np.array([
|
||||
UP+LEFT,
|
||||
DOWN+RIGHT,
|
||||
UP+RIGHT,
|
||||
DOWN+LEFT,
|
||||
])
|
||||
self.add(Line(p1, p2), Line(p3, p4))
|
||||
self.init_colors()
|
||||
|
||||
class CubicBezier(VMobject):
|
||||
def __init__(self, points, **kwargs):
|
||||
VMobject.__init__(self, **kwargs)
|
||||
|
@ -12,6 +12,13 @@ from topics.geometry import Circle, Line, Rectangle, Square, \
|
||||
Arc, Polygon, SurroundingRectangle
|
||||
from topics.three_dimensions import Cube
|
||||
|
||||
class Lightbulb(SVGMobject):
|
||||
CONFIG = {
|
||||
"file_name" : "lightbulb",
|
||||
"height" : 1,
|
||||
"stroke_color" : YELLOW,
|
||||
}
|
||||
|
||||
class BitcoinLogo(SVGMobject):
|
||||
CONFIG = {
|
||||
"file_name" : "Bitcoin_logo",
|
||||
|
Reference in New Issue
Block a user